From 9786567e45eee30e68af97926de96905e765e48d Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Thu, 12 Feb 2015 23:07:37 -0500 Subject: [PATCH 001/788] ext4: ignore journal checksum on remount; don't fail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 2d5b86e048780c5efa7f7d9708815555919e7b05 upstream. As of v3.18, ext4 started rejecting a remount which changes the journal_checksum option. Prior to that, it was simply ignored; the problem here is that if someone has this in their fstab for the root fs, now the box fails to boot properly, because remount of root with the new options will fail, and the box proceeds with a readonly root. I think it is a little nicer behavior to accept the option, but warn that it's being ignored, rather than failing the mount, but that might be a subjective matter... Reported-by: Cónräd Signed-off-by: Eric Sandeen Signed-off-by: Theodore Ts'o Cc: Josh Boyer Signed-off-by: Greg Kroah-Hartman --- fs/ext4/super.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 74c5f53595fbd1..fc29b2c91bef37 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -4864,9 +4864,8 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data) if ((old_opts.s_mount_opt & EXT4_MOUNT_JOURNAL_CHECKSUM) ^ test_opt(sb, JOURNAL_CHECKSUM)) { ext4_msg(sb, KERN_ERR, "changing journal_checksum " - "during remount not supported"); - err = -EINVAL; - goto restore_opts; + "during remount not supported; ignoring"); + sbi->s_mount_opt ^= EXT4_MOUNT_JOURNAL_CHECKSUM; } if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA) { From 0434848e444b31ea862743b720585c127b8114c2 Mon Sep 17 00:00:00 2001 From: Adam Lee Date: Wed, 28 Jan 2015 15:30:27 -0500 Subject: [PATCH 002/788] Bluetooth: ath3k: workaround the compatibility issue with xHCI controller commit c561a5753dd631920c4459a067d22679b3d110d6 upstream. BugLink: https://bugs.launchpad.net/bugs/1400215 ath3k devices fail to load firmwares on xHCI buses, but work well on EHCI, this might be a compatibility issue between xHCI and ath3k chips. As my testing result, those chips will work on xHCI buses again with this patch. This workaround is from Qualcomm, they also did some workarounds in Windows driver. Signed-off-by: Adam Lee Signed-off-by: Marcel Holtmann Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/ath3k.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index 1ee27ac18de052..f568d3df5ede35 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -174,6 +174,8 @@ static const struct usb_device_id ath3k_blist_tbl[] = { #define USB_REQ_DFU_DNLOAD 1 #define BULK_SIZE 4096 #define FW_HDR_SIZE 20 +#define TIMEGAP_USEC_MIN 50 +#define TIMEGAP_USEC_MAX 100 static int ath3k_load_firmware(struct usb_device *udev, const struct firmware *firmware) @@ -205,6 +207,9 @@ static int ath3k_load_firmware(struct usb_device *udev, pipe = usb_sndbulkpipe(udev, 0x02); while (count) { + /* workaround the compatibility issue with xHCI controller*/ + usleep_range(TIMEGAP_USEC_MIN, TIMEGAP_USEC_MAX); + size = min_t(uint, count, BULK_SIZE); memcpy(send_buf, firmware->data + sent, size); @@ -302,6 +307,9 @@ static int ath3k_load_fwfile(struct usb_device *udev, pipe = usb_sndbulkpipe(udev, 0x02); while (count) { + /* workaround the compatibility issue with xHCI controller*/ + usleep_range(TIMEGAP_USEC_MIN, TIMEGAP_USEC_MAX); + size = min_t(uint, count, BULK_SIZE); memcpy(send_buf, firmware->data + sent, size); From 89c3b66186131dd3da5a9d386301a6a062348b92 Mon Sep 17 00:00:00 2001 From: Dmitry Tunin Date: Sun, 18 Jan 2015 00:16:51 +0300 Subject: [PATCH 003/788] Bluetooth: ath3k: Add support of AR3012 bluetooth 13d3:3423 device commit 033efa920a7f22a8caf7a38d851a2f451781bbf7 upstream. Add support of 13d3:3423 device. BugLink: https://bugs.launchpad.net/bugs/1411193 T: Bus=01 Lev=02 Prnt=03 Port=00 Cnt=01 Dev#= 5 Spd=12 MxCh= 0 D: Ver= 1.10 Cls=e0(wlcon) Sub=01 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=13d3 ProdID=3423 Rev= 0.01 C:* #Ifs= 2 Cfg#= 1 Atr=e0 MxPwr=100mA A: FirstIf#= 0 IfCount= 2 Cls=e0(wlcon) Sub=01 Prot=01 I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=1ms E: Ad=82(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms I: If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms I: If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms I: If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms I: If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms I: If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms Signed-off-by: Dmitry Tunin Signed-off-by: Marcel Holtmann Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/ath3k.c | 2 ++ drivers/bluetooth/btusb.c | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index f568d3df5ede35..de4c8499cbac95 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -108,6 +108,7 @@ static const struct usb_device_id ath3k_table[] = { { USB_DEVICE(0x13d3, 0x3393) }, { USB_DEVICE(0x13d3, 0x3402) }, { USB_DEVICE(0x13d3, 0x3408) }, + { USB_DEVICE(0x13d3, 0x3423) }, { USB_DEVICE(0x13d3, 0x3432) }, /* Atheros AR5BBU12 with sflash firmware */ @@ -162,6 +163,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = { { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x13d3, 0x3423), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 }, /* Atheros AR5BBU22 with sflash firmware */ diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 19cf2cf22e8798..a701e9505b1705 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -188,6 +188,7 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x13d3, 0x3423), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 }, /* Atheros AR5BBU12 with sflash firmware */ From 5714d70283424ae8aef50fd381c6d1d630ad2fee Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 14 Jan 2015 20:51:37 +0200 Subject: [PATCH 004/788] Bluetooth: Fix valid Identity Address check commit e12af489b91d47a806f4e96e4edc20df612482e7 upstream. According to the Bluetooth core specification valid identity addresses are either Public Device Addresses or Static Random Addresses. IRKs received with any other type of address should be discarded since we cannot assume to know the permanent identity of the peer device. This patch fixes a missing check for the Identity Address when receiving the Identity Address Information SMP PDU. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/smp.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index b67749bb55bffa..757ae32bda4745 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -2303,8 +2303,12 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn, * implementations are not known of and in order to not over * complicate our implementation, simply pretend that we never * received an IRK for such a device. + * + * The Identity Address must also be a Static Random or Public + * Address, which hci_is_identity_address() checks for. */ - if (!bacmp(&info->bdaddr, BDADDR_ANY)) { + if (!bacmp(&info->bdaddr, BDADDR_ANY) || + !hci_is_identity_address(&info->bdaddr, info->addr_type)) { BT_ERR("Ignoring IRK with no identity address"); goto distribute; } From b5e12120205cf11c4f1cba646dd7eeb0bbc2ff35 Mon Sep 17 00:00:00 2001 From: Rick Dunn Date: Sat, 17 Jan 2015 05:29:12 +0100 Subject: [PATCH 005/788] Bluetooth: btusb: Add Broadcom patchram support for ASUSTek devices commit 9a5abdaaf9d2e80e157c7a756f9d9fd933dee48e upstream. T: Bus=03 Lev=01 Prnt=01 Port=06 Cnt=02 Dev#= 3 Spd=12 MxCh= 0 D: Ver= 2.00 Cls=ff(vend.) Sub=01 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=0b05 ProdID=17cf Rev= 1.12 S: Manufacturer=Broadcom Corp S: Product=BCM20702A0 S: SerialNumber=54271E3298CD C:* #Ifs= 4 Cfg#= 1 Atr=e0 MxPwr= 0mA I:* If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=01 Prot=01 Driver=btusb E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=1ms E: Ad=82(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms I: If#= 1 Alt= 1 #EPs= 2 Cls=ff(vend.) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms I: If#= 1 Alt= 2 #EPs= 2 Cls=ff(vend.) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms I: If#= 1 Alt= 3 #EPs= 2 Cls=ff(vend.) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms I: If#= 1 Alt= 4 #EPs= 2 Cls=ff(vend.) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms I: If#= 1 Alt= 5 #EPs= 2 Cls=ff(vend.) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms I:* If#= 2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none) E: Ad=84(I) Atr=02(Bulk) MxPS= 32 Ivl=0ms E: Ad=04(O) Atr=02(Bulk) MxPS= 32 Ivl=0ms I:* If#= 3 Alt= 0 #EPs= 0 Cls=fe(app. ) Sub=01 Prot=01 Driver=(none) Firmware is extracted from the latest Broadcom BCM4352 Windows driver by extracting the zip and searching the .hex file names for '17cf'. The hex file must then be converted to hcd format using the hex2hcd utility and then moved to /lib/firmware/brcm/. Signed-off-by: Rick Dunn Signed-off-by: Marcel Holtmann Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/btusb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index a701e9505b1705..e0dd990a9a411b 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -118,7 +118,8 @@ static const struct usb_device_id btusb_table[] = { .driver_info = BTUSB_BCM_PATCHRAM }, /* ASUSTek Computer - Broadcom based */ - { USB_VENDOR_AND_INTERFACE_INFO(0x0b05, 0xff, 0x01, 0x01) }, + { USB_VENDOR_AND_INTERFACE_INFO(0x0b05, 0xff, 0x01, 0x01), + .driver_info = BTUSB_BCM_PATCHRAM }, /* Belkin F8065bf - Broadcom based */ { USB_VENDOR_AND_INTERFACE_INFO(0x050d, 0xff, 0x01, 0x01) }, From 98f6b0026b59a590d2a79bec221a05b9016e1985 Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Thu, 22 Jan 2015 16:57:05 +0100 Subject: [PATCH 006/788] Bluetooth: Fix reporting invalid RSSI for LE devices commit 91200e9f3e76af2652952e73ce5d9913f1c987c6 upstream. Start Discovery was reporting 0 RSSI for invalid RSSI only for BR/EDR devices. LE devices were reported with RSSI 127. Signed-off-by: Szymon Janc Signed-off-by: Marcel Holtmann Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/mgmt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 693ce8bcd06e6e..1775dbfc84a821 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -7066,7 +7066,8 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, * However when using service discovery, the value 127 will be * returned when the RSSI is not available. */ - if (rssi == HCI_RSSI_INVALID && !hdev->discovery.report_invalid_rssi) + if (rssi == HCI_RSSI_INVALID && !hdev->discovery.report_invalid_rssi && + link_type == ACL_LINK) rssi = 0; bacpy(&ev->addr.bdaddr, bdaddr); From 3b71674f85b03d01effa3f39a401f0b57d0bac7f Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 26 Jan 2015 20:35:32 -0800 Subject: [PATCH 007/788] Bluetooth: btusb: Add support for Dynex/Insignia USB dongles commit d049f4e513e861167361b06c7ca85f9e872c8cde upstream. The Dynex/Insignia USB dongles are Broadcom BCM20702B0 based and require firmware update before operation. T: Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=12 MxCh= 0 D: Ver= 2.00 Cls=ff(vend.) Sub=01 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=19ff ProdID=0239 Rev= 1.12 S: Manufacturer=Broadcom Corp S: Product=BCM20702A0 C:* #Ifs= 4 Cfg#= 1 Atr=e0 MxPwr= 0mA I:* If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=01 Prot=01 Driver=btusb E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=1ms E: Ad=82(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms I: If#= 1 Alt= 1 #EPs= 2 Cls=ff(vend.) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms I: If#= 1 Alt= 2 #EPs= 2 Cls=ff(vend.) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms I: If#= 1 Alt= 3 #EPs= 2 Cls=ff(vend.) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms I: If#= 1 Alt= 4 #EPs= 2 Cls=ff(vend.) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms I: If#= 1 Alt= 5 #EPs= 2 Cls=ff(vend.) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms I:* If#= 2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none) E: Ad=84(I) Atr=02(Bulk) MxPS= 32 Ivl=0ms E: Ad=04(O) Atr=02(Bulk) MxPS= 32 Ivl=0ms I:* If#= 3 Alt= 0 #EPs= 0 Cls=fe(app. ) Sub=01 Prot=01 Driver=(none) Since this is an unsual USB vendor ID (0x19ff), these dongles are added via USB_DEVICE macro and not USB_VENDOR_AND_INTERFACE_INFO as done for mainstream Broadcom based dongles. The latest known working firmware is BCM20702B0_002.001.014.0527.0557.hex which needs to be converted using hex2hcd utility and then installed as /lib/firmware/brcm/BCM20702A0-19ff-0239.hcd to make this device fully operational. Bluetooth: hci0: BCM: patching hci_ver=06 hci_rev=2000 lmp_ver=06 lmp_subver=410e Bluetooth: hci0: BCM: firmware hci_ver=06 hci_rev=222d lmp_ver=06 lmp_subver=410e With this firmware the device reports support for connectionless slave broadcast (master and slave) feature used by 3D Glasses and TVs. < HCI Command: Read Local Extended Features (0x04|0x0004) plen 1 Page: 2 > HCI Event: Command Complete (0x0e) plen 14 Read Local Extended Features (0x04|0x0004) ncmd 1 Status: Success (0x00) Page: 2/2 Features: 0x0f 0x00 0x00 0x00 0x00 0x00 0x00 0x00 Connectionless Slave Broadcast - Master Connectionless Slave Broadcast - Slave Synchronization Train Synchronization Scan However there are some flaws with this feature. The Set Event Mask Page 2 command is actually not supported and with that all connectionless slave broadcast events are always enabled. < HCI Command: Set Event Mask Page 2 (0x03|0x0063) plen 8 Mask: 0x00000000000f0000 Synchronization Train Received Connectionless Slave Broadcast Receive Connectionless Slave Broadcast Timeout Truncated Page Complete > HCI Event: Command Complete (0x0e) plen 4 Set Event Mask Page 2 (0x03|0x0063) ncmd 1 Status: Unknown HCI Command (0x01) In addition the Synchronization Train Received event is actually broken on this controller. It mixes up the order of parameters. According to the Bluetooth Core specification the fields are like this: struct hci_ev_sync_train_received { __u8 status; bdaddr_t bdaddr; __le32 offset; __u8 map[10]; __u8 lt_addr; __le32 instant; __le16 interval; __u8 service_data; } __packed; This controller however sends the service_data as 5th parameter instead of having it as last parameter. struct hci_ev_sync_train_received { __u8 status; bdaddr_t bdaddr; __le32 offset; __u8 map[10]; __u8 service_data; __u8 lt_addr; __le32 instant; __le16 interval; } __packed; So anybody trying to use this hardware for utilizing connectionless slave broadcast receivers (aka 3D Glasses), be warned about this shortcoming. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/btusb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index e0dd990a9a411b..7a0732fe604dd6 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -109,6 +109,9 @@ static const struct usb_device_id btusb_table[] = { { USB_DEVICE(0x13d3, 0x3404), .driver_info = BTUSB_BCM_PATCHRAM }, + /* Broadcom BCM20702B0 (Dynex/Insignia) */ + { USB_DEVICE(0x19ff, 0x0239), .driver_info = BTUSB_BCM_PATCHRAM }, + /* Foxconn - Hon Hai */ { USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01), .driver_info = BTUSB_BCM_PATCHRAM }, From b302d582cff6f508a688556ccffa8ff0fcf4c1f6 Mon Sep 17 00:00:00 2001 From: Matej Dubovy Date: Mon, 2 Feb 2015 18:50:14 +0100 Subject: [PATCH 008/788] Bluetooth: btusb: Add support for Lite-On (04ca) Broadcom based, BCM43142 commit 8f0c304c693c5a9759ed6ae50d07d4590dad5ae7 upstream. Please add support for sub BT chip on the combo card Broadcom 43142A0 (in Lenovo E145), 04ca:2007 /sys/kernel/debug/usb/devices T: Bus=05 Lev=01 Prnt=01 Port=01 Cnt=02 Dev#= 3 Spd=12 MxCh= 0 D: Ver= 2.00 Cls=ff(vend.) Sub=01 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=04ca ProdID=2007 Rev= 1.12 S: Manufacturer=Broadcom Corp S: Product=BCM43142A0 S: SerialNumber=28E347EC73BD C:* #Ifs= 4 Cfg#= 1 Atr=e0 MxPwr= 0mA I:* If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=01 Prot=01 Driver=(none) E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=1ms E: Ad=82(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=01 Prot=01 Driver=(none) E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms I: If#= 1 Alt= 1 #EPs= 2 Cls=ff(vend.) Sub=01 Prot=01 Driver=(none) E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms I: If#= 1 Alt= 2 #EPs= 2 Cls=ff(vend.) Sub=01 Prot=01 Driver=(none) E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms I: If#= 1 Alt= 3 #EPs= 2 Cls=ff(vend.) Sub=01 Prot=01 Driver=(none) E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms I: If#= 1 Alt= 4 #EPs= 2 Cls=ff(vend.) Sub=01 Prot=01 Driver=(none) E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms I: If#= 1 Alt= 5 #EPs= 2 Cls=ff(vend.) Sub=01 Prot=01 Driver=(none) E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms I:* If#= 2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none) E: Ad=84(I) Atr=02(Bulk) MxPS= 32 Ivl=0ms E: Ad=04(O) Atr=02(Bulk) MxPS= 32 Ivl=0ms I:* If#= 3 Alt= 0 #EPs= 0 Cls=fe(app. ) Sub=01 Prot=01 Driver=(none) Firmware for 04ca:2007 can be extracted from the latest Lenovo E145 Bluetooth driver for Windows (driver is however described as BCM20702 but contains also firwmare for BCM43142). Search for BCM43142A0_001.001.011.0122.0153.hex within hex files, then it must be converted using hex2hcd utility. Rename file to BCM43142A0-04ca-2007.hcd, then move to /lib/firmware/brcm/. Signed-off-by: Matej Dubovy Signed-off-by: Marcel Holtmann Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/btusb.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 7a0732fe604dd6..c91ec52a89481b 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -116,6 +116,10 @@ static const struct usb_device_id btusb_table[] = { { USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01), .driver_info = BTUSB_BCM_PATCHRAM }, + /* Lite-On Technology - Broadcom based */ + { USB_VENDOR_AND_INTERFACE_INFO(0x04ca, 0xff, 0x01, 0x01), + .driver_info = BTUSB_BCM_PATCHRAM }, + /* Broadcom devices with vendor specific id */ { USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01), .driver_info = BTUSB_BCM_PATCHRAM }, From 8a3f71b3e173ef8be1200d29b8691b2d8ddd5681 Mon Sep 17 00:00:00 2001 From: George Spelvin Date: Sat, 7 Feb 2015 00:32:06 -0500 Subject: [PATCH 009/788] random: Fix fast_mix() function commit 19acc77a36970958a4a0e4daeb2c8cb2aab0ffd4 upstream. There was a bad typo in commit 43759d4f429c ("random: use an improved fast_mix() function") and I didn't notice because it "looked right", so I saw what I expected to see when I reviewed it. Only months later did I look and notice it's not the Threefish-inspired mix function that I had designed and optimized. Mea Culpa. Each input bit still has a chance to affect each output bit, and the fast pool is spilled *long* before it fills, so it's not a total disaster, but it's definitely not the intended great improvement. I'm still working on finding better rotation constants. These are good enough, but since it's unrolled twice, it's possible to get better mixing for free by using eight different constants rather than repeating the same four. Signed-off-by: George Spelvin Cc: Theodore Ts'o Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- drivers/char/random.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/char/random.c b/drivers/char/random.c index 04645c09fe5e5e..9cd6968e2f924b 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -569,19 +569,19 @@ static void fast_mix(struct fast_pool *f) __u32 c = f->pool[2], d = f->pool[3]; a += b; c += d; - b = rol32(a, 6); d = rol32(c, 27); + b = rol32(b, 6); d = rol32(d, 27); d ^= a; b ^= c; a += b; c += d; - b = rol32(a, 16); d = rol32(c, 14); + b = rol32(b, 16); d = rol32(d, 14); d ^= a; b ^= c; a += b; c += d; - b = rol32(a, 6); d = rol32(c, 27); + b = rol32(b, 6); d = rol32(d, 27); d ^= a; b ^= c; a += b; c += d; - b = rol32(a, 16); d = rol32(c, 14); + b = rol32(b, 16); d = rol32(d, 14); d ^= a; b ^= c; f->pool[0] = a; f->pool[1] = b; From aa7583d3c12bfc896f36ec06759aa42c29f14f85 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Thu, 22 Jan 2015 09:29:05 +1100 Subject: [PATCH 010/788] xfs: ensure buffer types are set correctly commit 0d612fb570b71ea2e49554a770cff4c489018b2c upstream. Jan Kara reported that log recovery was finding buffers with invalid types in them. This should not happen, and indicates a bug in the logging of buffers. To catch this, add asserts to the buffer formatting code to ensure that the buffer type is in range when the transaction is committed. We don't set a type on buffers being marked stale - they are not going to get replayed, the format item exists only for recovery to be able to prevent replay of the buffer, so the type does not matter. Hence that needs special casing here. Reported-by: Jan Kara Tested-by: Jan Kara Signed-off-by: Dave Chinner Reviewed-by: Brian Foster Signed-off-by: Dave Chinner Signed-off-by: Greg Kroah-Hartman --- fs/xfs/xfs_buf_item.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index 3f9bd58edec74e..744352bf324025 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -319,6 +319,10 @@ xfs_buf_item_format( ASSERT(atomic_read(&bip->bli_refcount) > 0); ASSERT((bip->bli_flags & XFS_BLI_LOGGED) || (bip->bli_flags & XFS_BLI_STALE)); + ASSERT((bip->bli_flags & XFS_BLI_STALE) || + (xfs_blft_from_flags(&bip->__bli_format) > XFS_BLFT_UNKNOWN_BUF + && xfs_blft_from_flags(&bip->__bli_format) < XFS_BLFT_MAX_BUF)); + /* * If it is an inode buffer, transfer the in-memory state to the From 082995eefbc77bad609f2b8bd704ee2c7a0cc4b8 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Thu, 22 Jan 2015 09:29:40 +1100 Subject: [PATCH 011/788] xfs: inode unlink does not set AGI buffer type commit f19b872b086711bb4b22c3a0f52f16aa920bcc61 upstream. This leads to log recovery throwing errors like: XFS (md0): Mounting V5 Filesystem XFS (md0): Starting recovery (logdev: internal) XFS (md0): Unknown buffer type 0! XFS (md0): _xfs_buf_ioapply: no ops on block 0xaea8802/0x1 ffff8800ffc53800: 58 41 47 49 ..... Which is the AGI buffer magic number. Ensure that we set the type appropriately in both unlink list addition and removal. Tested-by: Jan Kara Signed-off-by: Dave Chinner Reviewed-by: Brian Foster Signed-off-by: Dave Chinner Signed-off-by: Greg Kroah-Hartman --- fs/xfs/xfs_inode.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 41f804e740d715..d745e1a9bf2ef1 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -1995,6 +1995,7 @@ xfs_iunlink( agi->agi_unlinked[bucket_index] = cpu_to_be32(agino); offset = offsetof(xfs_agi_t, agi_unlinked) + (sizeof(xfs_agino_t) * bucket_index); + xfs_trans_buf_set_type(tp, agibp, XFS_BLFT_AGI_BUF); xfs_trans_log_buf(tp, agibp, offset, (offset + sizeof(xfs_agino_t) - 1)); return 0; @@ -2086,6 +2087,7 @@ xfs_iunlink_remove( agi->agi_unlinked[bucket_index] = cpu_to_be32(next_agino); offset = offsetof(xfs_agi_t, agi_unlinked) + (sizeof(xfs_agino_t) * bucket_index); + xfs_trans_buf_set_type(tp, agibp, XFS_BLFT_AGI_BUF); xfs_trans_log_buf(tp, agibp, offset, (offset + sizeof(xfs_agino_t) - 1)); } else { From cf631816ecc657cf7b616fd0b6ce56fbab91f086 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Thu, 22 Jan 2015 09:30:06 +1100 Subject: [PATCH 012/788] xfs: set buf types when converting extent formats commit fe22d552b82d7cc7de1851233ae8bef579198637 upstream. Conversion from local to extent format does not set the buffer type correctly on the new extent buffer when a symlink data is moved out of line. Fix the symlink code and leave a comment in the generic bmap code reminding us that the format-specific data copy needs to set the destination buffer type appropriately. Tested-by: Jan Kara Signed-off-by: Dave Chinner Reviewed-by: Brian Foster Signed-off-by: Dave Chinner Signed-off-by: Greg Kroah-Hartman --- fs/xfs/libxfs/xfs_bmap.c | 6 +++++- fs/xfs/libxfs/xfs_symlink_remote.c | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c index b5eb4743f75a03..4e20fe7497b325 100644 --- a/fs/xfs/libxfs/xfs_bmap.c +++ b/fs/xfs/libxfs/xfs_bmap.c @@ -973,7 +973,11 @@ xfs_bmap_local_to_extents( *firstblock = args.fsbno; bp = xfs_btree_get_bufl(args.mp, tp, args.fsbno, 0); - /* initialise the block and copy the data */ + /* + * Initialise the block and copy the data + * + * Note: init_fn must set the buffer log item type correctly! + */ init_fn(tp, bp, ip, ifp); /* account for the change in fork size and log everything */ diff --git a/fs/xfs/libxfs/xfs_symlink_remote.c b/fs/xfs/libxfs/xfs_symlink_remote.c index c80c5236c3da73..e7e26bd6468fdd 100644 --- a/fs/xfs/libxfs/xfs_symlink_remote.c +++ b/fs/xfs/libxfs/xfs_symlink_remote.c @@ -178,6 +178,8 @@ xfs_symlink_local_to_remote( struct xfs_mount *mp = ip->i_mount; char *buf; + xfs_trans_buf_set_type(tp, bp, XFS_BLFT_SYMLINK_BUF); + if (!xfs_sb_version_hascrc(&mp->m_sb)) { bp->b_ops = NULL; memcpy(bp->b_addr, ifp->if_u1.if_data, ifp->if_bytes); From 853e146c6ceb22840d4ed7679a5bff6b5c4f8308 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Thu, 22 Jan 2015 09:30:23 +1100 Subject: [PATCH 013/788] xfs: set superblock buffer type correctly commit 3443a3bca54588f43286b725d8648d33a38c86f1 upstream. When the superblock is modified in a transaction, the commonly modified fields are not actually copied to the superblock buffer to avoid the buffer lock becoming a serialisation point. However, there are some other operations that modify the superblock fields within the transaction that don't directly log to the superblock but rely on the changes to be applied during the transaction commit (to minimise the buffer lock hold time). When we do this, we fail to mark the buffer log item as being a superblock buffer and that can lead to the buffer not being marked with the corect type in the log and hence causing recovery issues. Fix it by setting the type correctly, similar to xfs_mod_sb()... Tested-by: Jan Kara Signed-off-by: Dave Chinner Reviewed-by: Brian Foster Signed-off-by: Dave Chinner Signed-off-by: Greg Kroah-Hartman --- fs/xfs/xfs_trans.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c index fa3135b9bf04b4..eb90cd59a0ec62 100644 --- a/fs/xfs/xfs_trans.c +++ b/fs/xfs/xfs_trans.c @@ -472,6 +472,7 @@ xfs_trans_apply_sb_deltas( whole = 1; } + xfs_trans_buf_set_type(tp, bp, XFS_BLFT_SB_BUF); if (whole) /* * Log the whole thing, the fields are noncontiguous. From b1d3ed10df50c11dcc6d9718efb42952d9efa2de Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Tue, 10 Feb 2015 09:23:40 +1100 Subject: [PATCH 014/788] xfs: only trace buffer items if they exist commit e9892d3cc853afdda2cc69e2576d9ddb5fafad71 upstream. The commit 2d3d0c5 ("xfs: lobotomise xfs_trans_read_buf_map()") left a landmine in the tracing code: trace_xfs_trans_buf_read() is now call on all buffers that are read through this interface rather than just buffers in transactions. For buffers outside transaction context, bp->b_fspriv is null, and so the buf log item tracing functions cannot be called. This causes a NULL pointer dereference in the trace_xfs_trans_buf_read() function when tracing is turned on. Signed-off-by: Dave Chinner Reviewed-by: Brian Foster Signed-off-by: Dave Chinner Signed-off-by: Greg Kroah-Hartman --- fs/xfs/xfs_trans_buf.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/xfs/xfs_trans_buf.c b/fs/xfs/xfs_trans_buf.c index 0a4d4ab6d9a9f6..75798412859a7b 100644 --- a/fs/xfs/xfs_trans_buf.c +++ b/fs/xfs/xfs_trans_buf.c @@ -327,9 +327,10 @@ xfs_trans_read_buf_map( return -EIO; } - if (tp) + if (tp) { _xfs_trans_bjoin(tp, bp, 1); - trace_xfs_trans_read_buf(bp->b_fspriv); + trace_xfs_trans_read_buf(bp->b_fspriv); + } *bpp = bp; return 0; From 694550cef4eb8021c4e5319c968db3b512626557 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Tue, 10 Feb 2015 14:08:32 -0800 Subject: [PATCH 015/788] fsnotify: fix handling of renames in audit commit 6ee8e25fc3e916193bce4ebb43d5439e1e2144ab upstream. Commit e9fd702a58c4 ("audit: convert audit watches to use fsnotify instead of inotify") broke handling of renames in audit. Audit code wants to update inode number of an inode corresponding to watched name in a directory. When something gets renamed into a directory to a watched name, inotify previously passed moved inode to audit code however new fsnotify code passes directory inode where the change happened. That confuses audit and it starts watching parent directory instead of a file in a directory. This can be observed for example by doing: cd /tmp touch foo bar auditctl -w /tmp/foo touch foo mv bar foo touch foo In audit log we see events like: type=CONFIG_CHANGE msg=audit(1423563584.155:90): auid=1000 ses=2 op="updated rules" path="/tmp/foo" key=(null) list=4 res=1 ... type=PATH msg=audit(1423563584.155:91): item=2 name="bar" inode=1046884 dev=08:0 2 mode=0100644 ouid=0 ogid=0 rdev=00:00 nametype=DELETE type=PATH msg=audit(1423563584.155:91): item=3 name="foo" inode=1046842 dev=08:0 2 mode=0100644 ouid=0 ogid=0 rdev=00:00 nametype=DELETE type=PATH msg=audit(1423563584.155:91): item=4 name="foo" inode=1046884 dev=08:0 2 mode=0100644 ouid=0 ogid=0 rdev=00:00 nametype=CREATE ... and that's it - we see event for the first touch after creating the audit rule, we see events for rename but we don't see any event for the last touch. However we start seeing events for unrelated stuff happening in /tmp. Fix the problem by passing moved inode as data in the FS_MOVED_FROM and FS_MOVED_TO events instead of the directory where the change happens. This doesn't introduce any new problems because noone besides audit_watch.c cares about the passed value: fs/notify/fanotify/fanotify.c cares only about FSNOTIFY_EVENT_PATH events. fs/notify/dnotify/dnotify.c doesn't care about passed 'data' value at all. fs/notify/inotify/inotify_fsnotify.c uses 'data' only for FSNOTIFY_EVENT_PATH. kernel/audit_tree.c doesn't care about passed 'data' at all. kernel/audit_watch.c expects moved inode as 'data'. Fixes: e9fd702a58c49db ("audit: convert audit watches to use fsnotify instead of inotify") Signed-off-by: Jan Kara Cc: Paul Moore Cc: Eric Paris Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- include/linux/fsnotify.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index 1c804b057fb109..7ee1774edee51c 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -101,8 +101,10 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir, new_dir_mask |= FS_ISDIR; } - fsnotify(old_dir, old_dir_mask, old_dir, FSNOTIFY_EVENT_INODE, old_name, fs_cookie); - fsnotify(new_dir, new_dir_mask, new_dir, FSNOTIFY_EVENT_INODE, new_name, fs_cookie); + fsnotify(old_dir, old_dir_mask, source, FSNOTIFY_EVENT_INODE, old_name, + fs_cookie); + fsnotify(new_dir, new_dir_mask, source, FSNOTIFY_EVENT_INODE, new_name, + fs_cookie); if (target) fsnotify_link_count(target); From 9d30ada41082fed20bddec4b623127cbbca06704 Mon Sep 17 00:00:00 2001 From: Calvin Owens Date: Tue, 13 Jan 2015 13:16:18 -0800 Subject: [PATCH 016/788] ksoftirqd: Enable IRQs and call cond_resched() before poking RCU commit 28423ad283d5348793b0c45cc9b1af058e776fd6 upstream. While debugging an issue with excessive softirq usage, I encountered the following note in commit 3e339b5dae24a706 ("softirq: Use hotplug thread infrastructure"): [ paulmck: Call rcu_note_context_switch() with interrupts enabled. ] ...but despite this note, the patch still calls RCU with IRQs disabled. This seemingly innocuous change caused a significant regression in softirq CPU usage on the sending side of a large TCP transfer (~1 GB/s): when introducing 0.01% packet loss, the softirq usage would jump to around 25%, spiking as high as 50%. Before the change, the usage would never exceed 5%. Moving the call to rcu_note_context_switch() after the cond_sched() call, as it was originally before the hotplug patch, completely eliminated this problem. Signed-off-by: Calvin Owens Signed-off-by: Paul E. McKenney Signed-off-by: Greg Kroah-Hartman --- kernel/softirq.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/kernel/softirq.c b/kernel/softirq.c index 501baa9ac1be3a..c497fcdf0d1e67 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -656,9 +656,13 @@ static void run_ksoftirqd(unsigned int cpu) * in the task stack here. */ __do_softirq(); - rcu_note_context_switch(); local_irq_enable(); cond_resched(); + + preempt_disable(); + rcu_note_context_switch(); + preempt_enable(); + return; } local_irq_enable(); From a91bf0c6f872d5215546c0215ed43acc5f77ff92 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 29 Jan 2015 21:34:00 +0200 Subject: [PATCH 017/788] iwlwifi: pcie: disable the SCD_BASE_ADDR when we resume from WoWLAN commit cd8f438405032ac8ff88bd8f2eca5e0c0063b14b upstream. The base address of the scheduler in the device's memory (SRAM) comes from two different sources. The periphery register and the alive notification from the firmware. We have a check in iwl_pcie_tx_start that ensures that they are the same. When we resume from WoWLAN, the firmware may have crashed for whatever reason. In that case, the whole device may be reset which means that the periphery register will hold a meaningless value. When we come to compare trans_pcie->scd_base_addr (which really holds the value we had when we loaded the WoWLAN firmware upon suspend) and the current value of the register, we don't see a match unsurprisingly. Trick the check to avoid a loud yet harmless WARN. Note that when the WoWLAN has crashed, we will see that in iwl_trans_pcie_d3_resume which will let the op_mode know. Once the op_mode is informed that the WowLAN firmware has crashed, it can't do much besides resetting the whole device. Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/iwlwifi/pcie/tx.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index 8a6c7a084aa127..76709f99a5c7ef 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -722,7 +722,12 @@ void iwl_trans_pcie_tx_reset(struct iwl_trans *trans) iwl_write_direct32(trans, FH_KW_MEM_ADDR_REG, trans_pcie->kw.dma >> 4); - iwl_pcie_tx_start(trans, trans_pcie->scd_base_addr); + /* + * Send 0 as the scd_base_addr since the device may have be reset + * while we were in WoWLAN in which case SCD_SRAM_BASE_ADDR will + * contain garbage. + */ + iwl_pcie_tx_start(trans, 0); } /* From f8edfda1e949eabd62c08f9dea9a2d70551484f4 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Fri, 16 Jan 2015 11:09:30 +0200 Subject: [PATCH 018/788] iwlwifi: mvm: validate tid and sta_id in ba_notif commit 2cee4762c528a9bd2cdff793197bf591a2196c11 upstream. These are coming from the FW and are used to access arrays. Bad values can cause an out of bounds access so discard such ba_notifs and warn. Signed-off-by: Eyal Shapira Signed-off-by: Emmanuel Grumbach Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/iwlwifi/mvm/tx.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index c59d07567d9041..650a5f0b94c753 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -930,6 +930,11 @@ int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, sta_id = ba_notif->sta_id; tid = ba_notif->tid; + if (WARN_ONCE(sta_id >= IWL_MVM_STATION_COUNT || + tid >= IWL_MAX_TID_COUNT, + "sta_id %d tid %d", sta_id, tid)) + return 0; + rcu_read_lock(); sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); From 556f81de3e223c85265a6bd46f1253fb9a59d2ef Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 27 Jan 2015 15:06:57 +0200 Subject: [PATCH 019/788] iwlwifi: mvm: fix failure path when power_update fails in add_interface commit fd66fc1cafd72ddf27dbec3a5e29e99839d1bc84 upstream. When iwl_mvm_power_update_mac() is called, we have already added the mac context, so if this call fails we should remove the mac. Fixes: commit e5e7aa8e2561 ('iwlwifi: mvm: refactor power code') Signed-off-by: Luciano Coelho Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 20915587c8207a..a7e9a91c50a871 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -1146,7 +1146,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, ret = iwl_mvm_power_update_mac(mvm); if (ret) - goto out_release; + goto out_remove_mac; /* beacon filtering */ ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0); From 65ac01f44992113017522f1c38f421fc87e65f7c Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 29 Jan 2015 12:48:20 +0200 Subject: [PATCH 020/788] iwlwifi: mvm: always use mac color zero commit 5523d11cc46393a1e61b7ef4a0b2d4e7ed9521e4 upstream. We don't really need to use different mac colors when adding mac contexts, because they're not used anywhere. In fact, the firmware doesn't accept 255 as a valid color, so we get into a SYSASSERT 0x3401 when we reach that. Remove the color increment to use always zero and avoid reaching 255. Signed-off-by: Luciano Coelho Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index a7e9a91c50a871..a704be0bd28815 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -707,9 +707,6 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac, mvmvif->uploaded = false; mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; - /* does this make sense at all? */ - mvmvif->color++; - spin_lock_bh(&mvm->time_event_lock); iwl_mvm_te_clear_data(mvm, &mvmvif->time_event_data); spin_unlock_bh(&mvm->time_event_lock); From 9933a927ded632fde949e0ef6f7a201534d2add8 Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Fri, 20 Feb 2015 11:45:11 -0600 Subject: [PATCH 021/788] HID: i2c-hid: Limit reads to wMaxInputLength bytes for input events commit 6d00f37e49d95e640a3937a4a1ae07dbe92a10cb upstream. d1c7e29e8d27 (HID: i2c-hid: prevent buffer overflow in early IRQ) changed hid_get_input() to read ihid->bufsize bytes, which can be more than wMaxInputLength. This is the case with the Dell XPS 13 9343, and it is causing events to be missed. In some cases the missed events are releases, which can cause the cursor to jump or freeze, among other problems. Limit the number of bytes read to min(wMaxInputLength, ihid->bufsize) to prevent such problems. Fixes: d1c7e29e8d27 "HID: i2c-hid: prevent buffer overflow in early IRQ" Signed-off-by: Seth Forshee Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/i2c-hid/i2c-hid.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index d43e967e75339e..5e72fc2428f0b2 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -370,7 +370,10 @@ static int i2c_hid_hwreset(struct i2c_client *client) static void i2c_hid_get_input(struct i2c_hid *ihid) { int ret, ret_size; - int size = ihid->bufsize; + int size = le16_to_cpu(ihid->hdesc.wMaxInputLength); + + if (size > ihid->bufsize) + size = ihid->bufsize; ret = i2c_master_recv(ihid->client, ihid->inbuf, size); if (ret != size) { From f7d75a1a358adccd0660824a5ed798386da82da4 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Tue, 2 Dec 2014 17:35:04 +0100 Subject: [PATCH 022/788] PCI: Generate uppercase hex for modalias var in uevent commit 145b3fe579db66fbe999a2bc3fd5b63dffe9636d upstream. Some implementations of modprobe fail to load the driver for a PCI device automatically because the "interface" part of the modalias from the kernel is lowercase, and the modalias from file2alias is uppercase. The "interface" is the low-order byte of the Class Code, defined in PCI r3.0, Appendix D. Most interface types defined in the spec do not use alpha characters, so they won't be affected. For example, 00h, 01h, 10h, 20h, etc. are unaffected. Print the "interface" byte of the Class Code in uppercase hex, as we already do for the Vendor ID, Device ID, Class, etc. Commit 89ec3dcf17fd ("PCI: Generate uppercase hex for modalias interface class") fixed only half of the problem. Some udev implementations rely on the uevent file and not the modalias file. Fixes: d1ded203adf1 ("PCI: add MODALIAS to hotplug event for pci devices") Fixes: 89ec3dcf17fd ("PCI: Generate uppercase hex for modalias interface class") Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Bjorn Helgaas Acked-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/pci/pci-driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 887e6bd95af715..09a66bad801838 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -1383,7 +1383,7 @@ static int pci_uevent(struct device *dev, struct kobj_uevent_env *env) if (add_uevent_var(env, "PCI_SLOT_NAME=%s", pci_name(pdev))) return -ENOMEM; - if (add_uevent_var(env, "MODALIAS=pci:v%08Xd%08Xsv%08Xsd%08Xbc%02Xsc%02Xi%02x", + if (add_uevent_var(env, "MODALIAS=pci:v%08Xd%08Xsv%08Xsd%08Xbc%02Xsc%02Xi%02X", pdev->vendor, pdev->device, pdev->subsystem_vendor, pdev->subsystem_device, (u8)(pdev->class >> 16), (u8)(pdev->class >> 8), From 2f3c31127beba573b83475638ba5bd3ea4fba1e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Mon, 19 Jan 2015 17:53:20 +0900 Subject: [PATCH 023/788] PCI: Fix infinite loop with ROM image of size 0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 16b036af31e1456cb69243a5a0c9ef801ecd1f17 upstream. If the image size would ever read as 0, pci_get_rom_size() could keep processing the same image over and over again. Exit the loop if we ever read a length of zero. This fixes a soft lockup on boot when the radeon driver calls pci_get_rom_size() on an AMD Radeon R7 250X PCIe discrete graphics card. [bhelgaas: changelog, reference] Link: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1386973 Reported-by: Federico Signed-off-by: Michel Dänzer Signed-off-by: Bjorn Helgaas Reviewed-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/pci/rom.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c index f955edb9bea7e8..eb0ad530dc4302 100644 --- a/drivers/pci/rom.c +++ b/drivers/pci/rom.c @@ -71,6 +71,7 @@ size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom, size_t size) { void __iomem *image; int last_image; + unsigned length; image = rom; do { @@ -93,9 +94,9 @@ size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom, size_t size) if (readb(pds + 3) != 'R') break; last_image = readb(pds + 21) & 0x80; - /* this length is reliable */ - image += readw(pds + 16) * 512; - } while (!last_image); + length = readw(pds + 16); + image += length * 512; + } while (length && !last_image); /* never return a size larger than the PCI resource window */ /* there are known ROMs that get the size wrong */ From 817e5fa0f64d6a087d9113e1f73b97f20cf99063 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 9 Feb 2015 14:41:50 +0800 Subject: [PATCH 024/788] ASoC: rt5670: Set use_single_rw flag for regmap commit 92b133f251b5f914f3ed28bc83e5b7a40d4e22ed upstream. RT5670 doesn't support auto incrementing writes so driver should set the use_single_rw flag for regmap. Signed-off-by: Bard Liao Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/rt5670.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index 8a0833de1665bc..1e574c85b9c8f2 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -2522,6 +2522,7 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5670 = { static const struct regmap_config rt5670_regmap = { .reg_bits = 8, .val_bits = 16, + .use_single_rw = true, .max_register = RT5670_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5670_ranges) * RT5670_PR_SPACING), .volatile_reg = rt5670_volatile_register, From e0a435dd7766ffc6ef3a01b8ff1d649422cbd33b Mon Sep 17 00:00:00 2001 From: Christian Engelmayer Date: Sat, 7 Feb 2015 23:40:52 +0100 Subject: [PATCH 025/788] ASoC: Intel: sst: Fix firmware name size handling commit 279e17ae81c17b40ae7a6c9e10f386a7aac7aa55 upstream. Function sst_acpi_probe() uses plain strcpy for setting member firmware_name of a struct intel_sst_drv from member firmware of a struct sst_machines. Thereby the destination array has got a length of 20 byte while the source may hold 32 byte. Since eg. commit 64b9c90b8600 ("ASoC: Intel: Fix BYTCR firmware name") increased strings from "fw_sst_0f28.bin" to "intel/fw_sst_0f28.bin" there is an actual possibility that the 20 byte array at the end of struct intel_sst_drv is overrun. Thus increase the size of the destination and use the same define for both structs. Detected by Coverity CID 1260087. Signed-off-by: Christian Engelmayer Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/intel/sst/sst.h | 3 ++- sound/soc/intel/sst/sst_acpi.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/sst/sst.h b/sound/soc/intel/sst/sst.h index 7f4bbfcbc6f552..562bc483d6b7f7 100644 --- a/sound/soc/intel/sst/sst.h +++ b/sound/soc/intel/sst/sst.h @@ -58,6 +58,7 @@ enum sst_algo_ops { #define SST_BLOCK_TIMEOUT 1000 #define FW_SIGNATURE_SIZE 4 +#define FW_NAME_SIZE 32 /* stream states */ enum sst_stream_states { @@ -426,7 +427,7 @@ struct intel_sst_drv { * Holder for firmware name. Due to async call it needs to be * persistent till worker thread gets called */ - char firmware_name[20]; + char firmware_name[FW_NAME_SIZE]; }; /* misc definitions */ diff --git a/sound/soc/intel/sst/sst_acpi.c b/sound/soc/intel/sst/sst_acpi.c index b3360139c41a90..51f83bad5319e1 100644 --- a/sound/soc/intel/sst/sst_acpi.c +++ b/sound/soc/intel/sst/sst_acpi.c @@ -47,7 +47,7 @@ struct sst_machines { char board[32]; char machine[32]; void (*machine_quirk)(void); - char firmware[32]; + char firmware[FW_NAME_SIZE]; struct sst_platform_info *pdata; }; From 2ce87d6e511c11b4cf86d5b8fce4790e046ecee1 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 28 Jan 2015 22:31:30 +0100 Subject: [PATCH 026/788] ASoC: rt5677: fix SPI dependency commit 4c121129c9dcb43b33d1cd568c8f2636e72597b0 upstream. The rt5677 codec has gained code that requires SPI to work correctly, but there is no provision in Kconfig to prevent the driver from being used when SPI is disabled or a loadable module, resulting in this build error: sound/built-in.o: In function `rt5677_spi_write': :(.text+0xa7ba0): undefined reference to `spi_sync' sound/built-in.o: In function `rt5677_spi_driver_init': :(.init.text+0x253c): undefined reference to `spi_register_driver' ERROR: "spi_sync" [sound/soc/codecs/snd-soc-rt5677-spi.ko] undefined! ERROR: "spi_register_driver" [sound/soc/codecs/snd-soc-rt5677-spi.ko] undefined! This makes the SPI portion of the driver depend on the SPI subsystem, and disables the function that uses SPI for firmware download if SPI is disabled. The latter may not be the correct solution, but I could not come up with a better one. Signed-off-by: Arnd Bergmann Fixes: af48f1d08a54741 ("ASoC: rt5677: Support DSP function for VAD application") Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/Kconfig | 2 +- sound/soc/codecs/rt5677.c | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 8349f982a58684..ef2c70e77d9181 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -525,7 +525,7 @@ config SND_SOC_RT5677 config SND_SOC_RT5677_SPI tristate - default SND_SOC_RT5677 + default SND_SOC_RT5677 && SPI #Freescale sgtl5000 codec config SND_SOC_SGTL5000 diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 918ada9738b043..cc9098830ed804 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -702,6 +702,9 @@ static int rt5677_set_dsp_vad(struct snd_soc_codec *codec, bool on) static bool activity; int ret; + if (!IS_ENABLED(CONFIG_SND_SOC_RT5677_SPI)) + return -ENXIO; + if (on && !activity) { activity = true; From 6393f60525cc47c7456b78ea5e2bc03e7d6c69f9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 15 Jan 2015 12:52:01 +0100 Subject: [PATCH 027/788] ASoC: mioa701_wm9713: Fix speaker event commit 7331ea474e9e7a348541c207bdb6aa518c6403f4 upstream. Commit f6b2a04590bb ("ASoC: pxa: mioa701_wm9713: Convert to table based DAPM setup") converted the driver to register the board level DAPM elements with the card's DAPM context rather than the CODEC's DAPM context. The change overlooked that the speaker widget event callback accesses the widget's codec field which is only valid if the widget has been registered in a CODEC DAPM context. This patch modifies the callback to take an alternative route to get the CODEC. Fixes: f6b2a04590bb ("ASoC: pxa: mioa701_wm9713: Convert to table based DAPM setup") Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/pxa/mioa701_wm9713.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c index 396dbd51a64f01..a9615a57454698 100644 --- a/sound/soc/pxa/mioa701_wm9713.c +++ b/sound/soc/pxa/mioa701_wm9713.c @@ -81,7 +81,7 @@ static int rear_amp_power(struct snd_soc_codec *codec, int power) static int rear_amp_event(struct snd_soc_dapm_widget *widget, struct snd_kcontrol *kctl, int event) { - struct snd_soc_codec *codec = widget->codec; + struct snd_soc_codec *codec = widget->dapm->card->rtd[0].codec; return rear_amp_power(codec, SND_SOC_DAPM_EVENT_ON(event)); } From 38e17b6477cf737704716ee567406a6e90be17aa Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 28 Jan 2015 22:30:01 +0100 Subject: [PATCH 028/788] ASoC: davinci: fix DM365_EVM codec selection commit f9a7ba326938f03b9305af8d31c360fce10cd4df upstream. An earlier bug fix of mine made the SND_DM365_VOICE_CODEC symbol tristate to avoid creating an undefined reference from the davinci-vcif.c driver to the davinci_soc_platform_register function that may be in a module. However, this may now lead to a different error on randconfig kernels: "warning: SND_DM365_VOICE_CODEC creates inconsistent choice state" This happens because we now have a choice statement with one bool and one tristate option, and the latter might not support being set to 'y' because of dependencies. This new change turns the other option into 'tristate' as well, which avoids the problem. Signed-off-by: Arnd Bergmann Fixes: 19926c6de0c3 ("ASoC: davinci: vcif must be a module if SND_DAVINCI_SOC is") Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/davinci/Kconfig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig index 8e948c63f3d96e..2b81ca418d2a67 100644 --- a/sound/soc/davinci/Kconfig +++ b/sound/soc/davinci/Kconfig @@ -58,13 +58,12 @@ choice depends on MACH_DAVINCI_DM365_EVM config SND_DM365_AIC3X_CODEC - bool "Audio Codec - AIC3101" + tristate "Audio Codec - AIC3101" help Say Y if you want to add support for AIC3101 audio codec config SND_DM365_VOICE_CODEC tristate "Voice Codec - CQ93VC" - depends on SND_DAVINCI_SOC select MFD_DAVINCI_VOICECODEC select SND_DAVINCI_SOC_VCIF select SND_SOC_CQ0093VC From af26d5bc5d6c017a8173933d5f1caf3e5ccb79eb Mon Sep 17 00:00:00 2001 From: Troy Tan Date: Tue, 20 Jan 2015 11:01:22 -0600 Subject: [PATCH 029/788] rtlwifi: rtl8192ee: Fix adhoc fail commit b661a5da57766f4f565d64238b753d6efc0f5499 upstream. When the buffer descriptor index exceeds 2, then a TX HANG condition will result. Signed-off-by: Troy Tan Signed-off-by: Larry Finger Signed-off-by: Kalle Valo Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/rtlwifi/rtl8192ee/fw.c | 6 +---- drivers/net/wireless/rtlwifi/rtl8192ee/hw.c | 26 --------------------- 2 files changed, 1 insertion(+), 31 deletions(-) diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/fw.c b/drivers/net/wireless/rtlwifi/rtl8192ee/fw.c index 45c128b91f7fec..c5d4b8013cdefe 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ee/fw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ee/fw.c @@ -666,7 +666,6 @@ void rtl92ee_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool b_dl_finished) struct sk_buff *skb = NULL; u32 totalpacketlen; - bool rtstatus; u8 u1rsvdpageloc[5] = { 0 }; bool b_dlok = false; @@ -728,10 +727,7 @@ void rtl92ee_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool b_dl_finished) memcpy((u8 *)skb_put(skb, totalpacketlen), &reserved_page_packet, totalpacketlen); - rtstatus = rtl_cmd_send_packet(hw, skb); - - if (rtstatus) - b_dlok = true; + b_dlok = true; if (b_dlok) { RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD , diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/hw.c b/drivers/net/wireless/rtlwifi/rtl8192ee/hw.c index 1a87edca2c3f36..3c27ec2c7b5a82 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ee/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ee/hw.c @@ -85,29 +85,6 @@ static void _rtl92ee_enable_bcn_sub_func(struct ieee80211_hw *hw) _rtl92ee_set_bcn_ctrl_reg(hw, 0, BIT(1)); } -static void _rtl92ee_return_beacon_queue_skb(struct ieee80211_hw *hw) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); - struct rtl8192_tx_ring *ring = &rtlpci->tx_ring[BEACON_QUEUE]; - unsigned long flags; - - spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags); - while (skb_queue_len(&ring->queue)) { - struct rtl_tx_buffer_desc *entry = - &ring->buffer_desc[ring->idx]; - struct sk_buff *skb = __skb_dequeue(&ring->queue); - - pci_unmap_single(rtlpci->pdev, - rtlpriv->cfg->ops->get_desc( - (u8 *)entry, true, HW_DESC_TXBUFF_ADDR), - skb->len, PCI_DMA_TODEVICE); - kfree_skb(skb); - ring->idx = (ring->idx + 1) % ring->entries; - } - spin_unlock_irqrestore(&rtlpriv->locks.irq_th_lock, flags); -} - static void _rtl92ee_disable_bcn_sub_func(struct ieee80211_hw *hw) { _rtl92ee_set_bcn_ctrl_reg(hw, BIT(1), 0); @@ -403,9 +380,6 @@ static void _rtl92ee_download_rsvd_page(struct ieee80211_hw *hw) rtl_write_byte(rtlpriv, REG_DWBCN0_CTRL + 2, bcnvalid_reg | BIT(0)); - /* Return Beacon TCB */ - _rtl92ee_return_beacon_queue_skb(hw); - /* download rsvd page */ rtl92ee_set_fw_rsvdpagepkt(hw, false); From 379a4a03856d68ef29f259b82f7dde98fda68006 Mon Sep 17 00:00:00 2001 From: Troy Tan Date: Tue, 20 Jan 2015 11:01:23 -0600 Subject: [PATCH 030/788] rtlwifi: rtl8192ee: Fix TX hang due to failure to update TX write point commit 6e5f4436162848289f071be38ee6b87dc8ea653d upstream. Initially, the routine to update the write point in the FIFO buffer was coded to save CPU time by not doing the calculation every interrupt. This was an error and results in TX hangs. Signed-off-by: Troy Tan Signed-off-by: Larry Finger Signed-off-by: Kalle Valo Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/rtlwifi/rtl8192ee/trx.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c b/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c index 2fcbef1d029fa5..147e2dd5fc15b9 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c @@ -1207,8 +1207,7 @@ bool rtl92ee_is_tx_desc_closed(struct ieee80211_hw *hw, u8 hw_queue, u16 index) static u8 stop_report_cnt; struct rtl8192_tx_ring *ring = &rtlpci->tx_ring[hw_queue]; - /*checking Read/Write Point each interrupt wastes CPU */ - if (stop_report_cnt > 15 || !rtlpriv->link_info.busytraffic) { + { u16 point_diff = 0; u16 cur_tx_rp, cur_tx_wp; u32 tmpu32 = 0; From 3d7520ce4275c3b1b0580222d623393acb2b55c9 Mon Sep 17 00:00:00 2001 From: Troy Tan Date: Tue, 20 Jan 2015 11:01:24 -0600 Subject: [PATCH 031/788] rtlwifi: rtl8192ee: Fix parsing of received packet commit 92ff754240b892cbc16dee5aa080322f3db88b68 upstream. The firmware supplies two kinds of packets via the RX mechanism. Besides the normal data received over the air, these packets may contain bluetooth status and other information. The present code fails to detect which kind of information was received. Signed-off-by: Troy Tan Signed-off-by: Larry Finger Signed-off-by: Kalle Valo Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/rtlwifi/rtl8192ee/trx.c | 4 ++++ drivers/net/wireless/rtlwifi/rtl8192ee/trx.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c b/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c index 147e2dd5fc15b9..03def29efb7f15 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c @@ -512,6 +512,10 @@ bool rtl92ee_rx_query_desc(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr; u32 phystatus = GET_RX_DESC_PHYST(pdesc); + if (GET_RX_STATUS_DESC_RPT_SEL(pdesc) == 0) + status->packet_report_type = NORMAL_RX; + else + status->packet_report_type = C2H_PACKET; status->length = (u16)GET_RX_DESC_PKT_LEN(pdesc); status->rx_drvinfo_size = (u8)GET_RX_DESC_DRV_INFO_SIZE(pdesc) * RX_DRV_INFO_SIZE_UNIT; diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/trx.h b/drivers/net/wireless/rtlwifi/rtl8192ee/trx.h index 6f9be1c7515c1c..8effef9b13dd68 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ee/trx.h +++ b/drivers/net/wireless/rtlwifi/rtl8192ee/trx.h @@ -542,6 +542,8 @@ LE_BITS_TO_4BYTE(__pdesc+8, 12, 4) #define GET_RX_DESC_RX_IS_QOS(__pdesc) \ LE_BITS_TO_4BYTE(__pdesc+8, 16, 1) +#define GET_RX_STATUS_DESC_RPT_SEL(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+8, 28, 1) #define GET_RX_DESC_RXMCS(__pdesc) \ LE_BITS_TO_4BYTE(__pdesc+12, 0, 7) From c908ec52b0de8b4fdaf8ad05ac27da7cf579e586 Mon Sep 17 00:00:00 2001 From: Troy Tan Date: Tue, 20 Jan 2015 11:01:26 -0600 Subject: [PATCH 032/788] rtlwifi: rtl8192ee: Fix DMA stalls commit 21b39ddb5bb2294fe64fbd29045591fe0707825f upstream. There are instances where the DMA engine stalls. The new code detects such stalls and restarts DMA without needing a power reset. Signed-off-by: Troy Tan Signed-off-by: Larry Finger Signed-off-by: Kalle Valo Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/rtlwifi/rtl8192ee/hw.c | 140 +++++++++++++++++++ drivers/net/wireless/rtlwifi/rtl8192ee/reg.h | 2 + 2 files changed, 142 insertions(+) diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/hw.c b/drivers/net/wireless/rtlwifi/rtl8192ee/hw.c index 3c27ec2c7b5a82..b461b3128da581 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ee/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ee/hw.c @@ -1137,6 +1137,139 @@ void rtl92ee_enable_hw_security_config(struct ieee80211_hw *hw) rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_WPA_CONFIG, &sec_reg_value); } +static bool _rtl8192ee_check_pcie_dma_hang(struct rtl_priv *rtlpriv) +{ + u8 tmp; + + /* write reg 0x350 Bit[26]=1. Enable debug port. */ + tmp = rtl_read_byte(rtlpriv, REG_BACKDOOR_DBI_DATA + 3); + if (!(tmp & BIT(2))) { + rtl_write_byte(rtlpriv, REG_BACKDOOR_DBI_DATA + 3, + tmp | BIT(2)); + mdelay(100); /* Suggested by DD Justin_tsai. */ + } + + /* read reg 0x350 Bit[25] if 1 : RX hang + * read reg 0x350 Bit[24] if 1 : TX hang + */ + tmp = rtl_read_byte(rtlpriv, REG_BACKDOOR_DBI_DATA + 3); + if ((tmp & BIT(0)) || (tmp & BIT(1))) { + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "CheckPcieDMAHang8192EE(): true!!\n"); + return true; + } + return false; +} + +static void _rtl8192ee_reset_pcie_interface_dma(struct rtl_priv *rtlpriv, + bool mac_power_on) +{ + u8 tmp; + bool release_mac_rx_pause; + u8 backup_pcie_dma_pause; + + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "ResetPcieInterfaceDMA8192EE()\n"); + + /* Revise Note: Follow the document "PCIe RX DMA Hang Reset Flow_v03" + * released by SD1 Alan. + */ + + /* 1. disable register write lock + * write 0x1C bit[1:0] = 2'h0 + * write 0xCC bit[2] = 1'b1 + */ + tmp = rtl_read_byte(rtlpriv, REG_RSV_CTRL); + tmp &= ~(BIT(1) | BIT(0)); + rtl_write_byte(rtlpriv, REG_RSV_CTRL, tmp); + tmp = rtl_read_byte(rtlpriv, REG_PMC_DBG_CTRL2); + tmp |= BIT(2); + rtl_write_byte(rtlpriv, REG_PMC_DBG_CTRL2, tmp); + + /* 2. Check and pause TRX DMA + * write 0x284 bit[18] = 1'b1 + * write 0x301 = 0xFF + */ + tmp = rtl_read_byte(rtlpriv, REG_RXDMA_CONTROL); + if (tmp & BIT(2)) { + /* Already pause before the function for another reason. */ + release_mac_rx_pause = false; + } else { + rtl_write_byte(rtlpriv, REG_RXDMA_CONTROL, (tmp | BIT(2))); + release_mac_rx_pause = true; + } + + backup_pcie_dma_pause = rtl_read_byte(rtlpriv, REG_PCIE_CTRL_REG + 1); + if (backup_pcie_dma_pause != 0xFF) + rtl_write_byte(rtlpriv, REG_PCIE_CTRL_REG + 1, 0xFF); + + if (mac_power_on) { + /* 3. reset TRX function + * write 0x100 = 0x00 + */ + rtl_write_byte(rtlpriv, REG_CR, 0); + } + + /* 4. Reset PCIe DMA + * write 0x003 bit[0] = 0 + */ + tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); + tmp &= ~(BIT(0)); + rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, tmp); + + /* 5. Enable PCIe DMA + * write 0x003 bit[0] = 1 + */ + tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); + tmp |= BIT(0); + rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, tmp); + + if (mac_power_on) { + /* 6. enable TRX function + * write 0x100 = 0xFF + */ + rtl_write_byte(rtlpriv, REG_CR, 0xFF); + + /* We should init LLT & RQPN and + * prepare Tx/Rx descrptor address later + * because MAC function is reset. + */ + } + + /* 7. Restore PCIe autoload down bit + * write 0xF8 bit[17] = 1'b1 + */ + tmp = rtl_read_byte(rtlpriv, REG_MAC_PHY_CTRL_NORMAL + 2); + tmp |= BIT(1); + rtl_write_byte(rtlpriv, REG_MAC_PHY_CTRL_NORMAL + 2, tmp); + + /* In MAC power on state, BB and RF maybe in ON state, + * if we release TRx DMA here + * it will cause packets to be started to Tx/Rx, + * so we release Tx/Rx DMA later. + */ + if (!mac_power_on) { + /* 8. release TRX DMA + * write 0x284 bit[18] = 1'b0 + * write 0x301 = 0x00 + */ + if (release_mac_rx_pause) { + tmp = rtl_read_byte(rtlpriv, REG_RXDMA_CONTROL); + rtl_write_byte(rtlpriv, REG_RXDMA_CONTROL, + (tmp & (~BIT(2)))); + } + rtl_write_byte(rtlpriv, REG_PCIE_CTRL_REG + 1, + backup_pcie_dma_pause); + } + + /* 9. lock system register + * write 0xCC bit[2] = 1'b0 + */ + tmp = rtl_read_byte(rtlpriv, REG_PMC_DBG_CTRL2); + tmp &= ~(BIT(2)); + rtl_write_byte(rtlpriv, REG_PMC_DBG_CTRL2, tmp); +} + int rtl92ee_hw_init(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -1162,6 +1295,13 @@ int rtl92ee_hw_init(struct ieee80211_hw *hw) rtlhal->fw_ps_state = FW_PS_STATE_ALL_ON_92E; } + if (_rtl8192ee_check_pcie_dma_hang(rtlpriv)) { + RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "92ee dma hang!\n"); + _rtl8192ee_reset_pcie_interface_dma(rtlpriv, + rtlhal->mac_func_enable); + rtlhal->mac_func_enable = false; + } + rtstatus = _rtl92ee_init_mac(hw); rtl_write_byte(rtlpriv, 0x577, 0x03); diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/reg.h b/drivers/net/wireless/rtlwifi/rtl8192ee/reg.h index 3f2a9596e7cd89..1eaa1fab550dd1 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ee/reg.h +++ b/drivers/net/wireless/rtlwifi/rtl8192ee/reg.h @@ -77,9 +77,11 @@ #define REG_HIMRE 0x00B8 #define REG_HISRE 0x00BC +#define REG_PMC_DBG_CTRL2 0x00CC #define REG_EFUSE_ACCESS 0x00CF #define REG_HPON_FSM 0x00EC #define REG_SYS_CFG1 0x00F0 +#define REG_MAC_PHY_CTRL_NORMAL 0x00F8 #define REG_SYS_CFG2 0x00FC #define REG_CR 0x0100 From 7ff4cadbeba30a716a7ea1415a86584b188fcd3e Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 3 Feb 2015 11:15:18 -0600 Subject: [PATCH 033/788] rtlwifi: rtl8192ee: Fix problems with calculating free space in FIFO commit 6d4beca3775222884e1ee9d48ef586c438c3dfa1 upstream. This driver utilizes a FIFO buffer for RX descriptors. There are four places in the code where it calculates the number of free slots. Several of those locations do the calculation incorrectly. To fix these and to prevent future mistakes, a common inline routine is created. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/rtlwifi/pci.h | 7 +++++++ drivers/net/wireless/rtlwifi/rtl8192ee/trx.c | 9 +-------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/rtlwifi/pci.h b/drivers/net/wireless/rtlwifi/pci.h index 5e832306dba956..d4567d12e07ebd 100644 --- a/drivers/net/wireless/rtlwifi/pci.h +++ b/drivers/net/wireless/rtlwifi/pci.h @@ -325,4 +325,11 @@ static inline void pci_write32_async(struct rtl_priv *rtlpriv, writel(val, (u8 __iomem *) rtlpriv->io.pci_mem_start + addr); } +static inline u16 calc_fifo_space(u16 rp, u16 wp) +{ + if (rp <= wp) + return RTL_PCI_MAX_RX_COUNT - 1 + rp - wp; + return rp - wp - 1; +} + #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c b/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c index 03def29efb7f15..00690040be3781 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c @@ -658,14 +658,7 @@ u16 rtl92ee_rx_desc_buff_remained_cnt(struct ieee80211_hw *hw, u8 queue_index) if (!start_rx) return 0; - if ((last_read_point > (RX_DESC_NUM_92E / 2)) && - (read_point <= (RX_DESC_NUM_92E / 2))) { - remind_cnt = RX_DESC_NUM_92E - write_point; - } else { - remind_cnt = (read_point >= write_point) ? - (read_point - write_point) : - (RX_DESC_NUM_92E - write_point + read_point); - } + remind_cnt = calc_fifo_space(read_point, write_point); if (remind_cnt == 0) return 0; From 43e591583b35a3bd5139124709eb72633ea41fe4 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 20 Jan 2015 11:01:20 -0600 Subject: [PATCH 034/788] rtlwifi: Remove logging statement that is no longer needed commit aeb2d2a4c0ae1739a6e1782bd8c1c96aee8db4e1 upstream. In commit e9538cf4f907 ("rtlwifi: Fix error when accessing unmapped memory in skb"), a printk was included to indicate that the condition had been reached. There is now enough evidence from other users that the fix is working. That logging statement can now be removed. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/rtlwifi/pci.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/net/wireless/rtlwifi/pci.c b/drivers/net/wireless/rtlwifi/pci.c index c70efb9a6e78cc..e25faacf58b768 100644 --- a/drivers/net/wireless/rtlwifi/pci.c +++ b/drivers/net/wireless/rtlwifi/pci.c @@ -816,11 +816,8 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw) /* get a new skb - if fail, old one will be reused */ new_skb = dev_alloc_skb(rtlpci->rxbuffersize); - if (unlikely(!new_skb)) { - pr_err("Allocation of new skb failed in %s\n", - __func__); + if (unlikely(!new_skb)) goto no_new; - } if (rtlpriv->use_new_trx_flow) { buffer_desc = &rtlpci->rx_ring[rxring_idx].buffer_desc From 6174cdc7baca9aba7d22891391c9ab085c19ecc8 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Sat, 31 Jan 2015 06:02:44 +0530 Subject: [PATCH 035/788] cpufreq: Set cpufreq_cpu_data to NULL before putting kobject commit 6ffae8c06fab058d6c3f8ecb7f921327721034e7 upstream. In __cpufreq_remove_dev_finish(), per-cpu 'cpufreq_cpu_data' needs to be cleared before calling kobject_put(&policy->kobj) and under cpufreq_driver_lock. Otherwise, if someone else calls cpufreq_cpu_get() in parallel with it, they can obtain a non-NULL policy from that after kobject_put(&policy->kobj) was executed. Consider this case: Thread A Thread B cpufreq_cpu_get() acquire cpufreq_driver_lock read-per-cpu cpufreq_cpu_data kobject_put(&policy->kobj); kobject_get(&policy->kobj); ... per_cpu(&cpufreq_cpu_data, cpu) = NULL And this will result in a warning like this one: ------------[ cut here ]------------ WARNING: CPU: 0 PID: 4 at include/linux/kref.h:47 kobject_get+0x41/0x50() Modules linked in: acpi_cpufreq(+) nfsd auth_rpcgss nfs_acl lockd grace sunrpc xfs libcrc32c sd_mod ixgbe igb mdio ahci hwmon ... Call Trace: [] dump_stack+0x46/0x58 [] warn_slowpath_common+0x81/0xa0 [] warn_slowpath_null+0x1a/0x20 [] kobject_get+0x41/0x50 [] cpufreq_cpu_get+0x75/0xc0 [] cpufreq_update_policy+0x2e/0x1f0 [] ? up+0x32/0x50 [] ? acpi_ns_get_node+0xcb/0xf2 [] ? acpi_evaluate_object+0x22c/0x252 [] ? acpi_get_handle+0x95/0xc0 [] ? acpi_has_method+0x25/0x40 [] acpi_processor_ppc_has_changed+0x77/0x82 [] ? move_linked_works+0x66/0x90 [] acpi_processor_notify+0x58/0xe7 [] acpi_ev_notify_dispatch+0x44/0x5c [] acpi_os_execute_deferred+0x15/0x22 [] process_one_work+0x160/0x410 [] worker_thread+0x11b/0x520 [] ? rescuer_thread+0x380/0x380 [] kthread+0xe1/0x100 [] ? kthread_create_on_node+0x1b0/0x1b0 [] ret_from_fork+0x7c/0xb0 [] ? kthread_create_on_node+0x1b0/0x1b0 ---[ end trace 89e66eb9795efdf7 ]--- The actual code flow is as follows: Thread A: Workqueue: kacpi_notify acpi_processor_notify() acpi_processor_ppc_has_changed() cpufreq_update_policy() cpufreq_cpu_get() kobject_get() Thread B: xenbus_thread() xenbus_thread() msg->u.watch.handle->callback() handle_vcpu_hotplug_event() vcpu_hotplug() cpu_down() __cpu_notify(CPU_POST_DEAD..) cpufreq_cpu_callback() __cpufreq_remove_dev_finish() cpufreq_policy_put_kobj() kobject_put() cpufreq_cpu_get() gets the policy from per-cpu variable cpufreq_cpu_data under cpufreq_driver_lock, and once it gets a valid policy it expects it to not be freed until cpufreq_cpu_put() is called. But the race happens when another thread puts the kobject first and updates cpufreq_cpu_data before or later. And so the first thread gets a valid policy structure and before it does kobject_get() on it, the second one has already done kobject_put(). Fix this by setting cpufreq_cpu_data to NULL before putting the kobject and that too under locks. Reported-by: Ethan Zhao Reported-by: Santosh Shilimkar Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/cpufreq/cpufreq.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 46bed4f81cde88..7030c409be24bd 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1416,9 +1416,10 @@ static int __cpufreq_remove_dev_finish(struct device *dev, unsigned long flags; struct cpufreq_policy *policy; - read_lock_irqsave(&cpufreq_driver_lock, flags); + write_lock_irqsave(&cpufreq_driver_lock, flags); policy = per_cpu(cpufreq_cpu_data, cpu); - read_unlock_irqrestore(&cpufreq_driver_lock, flags); + per_cpu(cpufreq_cpu_data, cpu) = NULL; + write_unlock_irqrestore(&cpufreq_driver_lock, flags); if (!policy) { pr_debug("%s: No cpu_data found\n", __func__); @@ -1473,7 +1474,6 @@ static int __cpufreq_remove_dev_finish(struct device *dev, } } - per_cpu(cpufreq_cpu_data, cpu) = NULL; return 0; } From 21ec654ecf736f21d3782773e0284ee837730fec Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Mon, 9 Feb 2015 13:38:17 -0500 Subject: [PATCH 036/788] cpufreq: speedstep-smi: enable interrupts when waiting commit d4d4eda23794c701442e55129dd4f8f2fefd5e4d upstream. On Dell Latitude C600 laptop with Pentium 3 850MHz processor, the speedstep-smi driver sometimes loads and sometimes doesn't load with "change to state X failed" message. The hardware sometimes refuses to change frequency and in this case, we need to retry later. I found out that we need to enable interrupts while waiting. When we enable interrupts, the hardware blockage that prevents frequency transition resolves and the transition is possible. With disabled interrupts, the blockage doesn't resolve (no matter how long do we wait). The exact reasons for this hardware behavior are unknown. This patch enables interrupts in the function speedstep_set_state that can be called with disabled interrupts. However, this function is called with disabled interrupts only from speedstep_get_freqs, so it shouldn't cause any problem. Signed-off-by: Mikulas Patocka Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/cpufreq/speedstep-lib.c | 3 +++ drivers/cpufreq/speedstep-smi.c | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/drivers/cpufreq/speedstep-lib.c b/drivers/cpufreq/speedstep-lib.c index 7047821a7f8a5f..4ab7a215667249 100644 --- a/drivers/cpufreq/speedstep-lib.c +++ b/drivers/cpufreq/speedstep-lib.c @@ -400,6 +400,7 @@ unsigned int speedstep_get_freqs(enum speedstep_processor processor, pr_debug("previous speed is %u\n", prev_speed); + preempt_disable(); local_irq_save(flags); /* switch to low state */ @@ -464,6 +465,8 @@ unsigned int speedstep_get_freqs(enum speedstep_processor processor, out: local_irq_restore(flags); + preempt_enable(); + return ret; } EXPORT_SYMBOL_GPL(speedstep_get_freqs); diff --git a/drivers/cpufreq/speedstep-smi.c b/drivers/cpufreq/speedstep-smi.c index 5fc96d5d656baf..819229e824fb69 100644 --- a/drivers/cpufreq/speedstep-smi.c +++ b/drivers/cpufreq/speedstep-smi.c @@ -156,6 +156,7 @@ static void speedstep_set_state(unsigned int state) return; /* Disable IRQs */ + preempt_disable(); local_irq_save(flags); command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff); @@ -166,9 +167,19 @@ static void speedstep_set_state(unsigned int state) do { if (retry) { + /* + * We need to enable interrupts, otherwise the blockage + * won't resolve. + * + * We disable preemption so that other processes don't + * run. If other processes were running, they could + * submit more DMA requests, making the blockage worse. + */ pr_debug("retry %u, previous result %u, waiting...\n", retry, result); + local_irq_enable(); mdelay(retry * 50); + local_irq_disable(); } retry++; __asm__ __volatile__( @@ -185,6 +196,7 @@ static void speedstep_set_state(unsigned int state) /* enable IRQs */ local_irq_restore(flags); + preempt_enable(); if (new_state == state) pr_debug("change to %u MHz succeeded after %u tries " From 86e777205efbc6c2acfafa08dc92588d0a88328b Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 18 Feb 2015 21:55:03 +0100 Subject: [PATCH 037/788] cpufreq: s3c: remove incorrect __init annotations commit 61882b63171736571e1139ab5aa929e3bb336016 upstream. The two functions s3c2416_cpufreq_driver_init and s3c_cpufreq_register are marked init but are called from a context that might be run after the __init sections are discarded, as the compiler points out: WARNING: vmlinux.o(.data+0x1ad9dc): Section mismatch in reference from the variable s3c2416_cpufreq_driver to the function .init.text:s3c2416_cpufreq_driver_init() WARNING: drivers/built-in.o(.text+0x35b5dc): Section mismatch in reference from the function s3c2410a_cpufreq_add() to the function .init.text:s3c_cpufreq_register() This removes the __init markings. Signed-off-by: Arnd Bergmann Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/cpufreq/s3c2416-cpufreq.c | 4 ++-- drivers/cpufreq/s3c24xx-cpufreq.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/cpufreq/s3c2416-cpufreq.c b/drivers/cpufreq/s3c2416-cpufreq.c index 2fd53eaaec20bf..d6d425773fa497 100644 --- a/drivers/cpufreq/s3c2416-cpufreq.c +++ b/drivers/cpufreq/s3c2416-cpufreq.c @@ -263,7 +263,7 @@ static int s3c2416_cpufreq_set_target(struct cpufreq_policy *policy, } #ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE -static void __init s3c2416_cpufreq_cfg_regulator(struct s3c2416_data *s3c_freq) +static void s3c2416_cpufreq_cfg_regulator(struct s3c2416_data *s3c_freq) { int count, v, i, found; struct cpufreq_frequency_table *pos; @@ -333,7 +333,7 @@ static struct notifier_block s3c2416_cpufreq_reboot_notifier = { .notifier_call = s3c2416_cpufreq_reboot_notifier_evt, }; -static int __init s3c2416_cpufreq_driver_init(struct cpufreq_policy *policy) +static int s3c2416_cpufreq_driver_init(struct cpufreq_policy *policy) { struct s3c2416_data *s3c_freq = &s3c2416_cpufreq; struct cpufreq_frequency_table *pos; diff --git a/drivers/cpufreq/s3c24xx-cpufreq.c b/drivers/cpufreq/s3c24xx-cpufreq.c index d00f1cee45094a..bd340a1ca87da5 100644 --- a/drivers/cpufreq/s3c24xx-cpufreq.c +++ b/drivers/cpufreq/s3c24xx-cpufreq.c @@ -454,7 +454,7 @@ static struct cpufreq_driver s3c24xx_driver = { }; -int __init s3c_cpufreq_register(struct s3c_cpufreq_info *info) +int s3c_cpufreq_register(struct s3c_cpufreq_info *info) { if (!info || !info->name) { printk(KERN_ERR "%s: failed to pass valid information\n", From b34efde33ef98c9f20e005c9288012d01f8cd3dc Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 18 Feb 2015 21:55:53 +0100 Subject: [PATCH 038/788] cpufreq: s3c: remove last use of resume_clocks callback commit 67fadaa2768716209ee19a8b8bf05bc3ac399445 upstream. Commit 32726d2d550 ("ARM: SAMSUNG: Remove legacy clock code") already removed the callback pointer, but there was one remaining user: drivers/cpufreq/s3c24xx-cpufreq.c: In function 's3c_cpufreq_resume_clocks': drivers/cpufreq/s3c24xx-cpufreq.c:149:14: error: 'struct s3c_cpufreq_info' has no member named 'resume_clocks' cpu_cur.info->resume_clocks(); ^ Signed-off-by: Arnd Bergmann Fixes: 32726d2d550 ("ARM: SAMSUNG: Remove legacy clock code") Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/cpufreq/s3c24xx-cpufreq.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/cpufreq/s3c24xx-cpufreq.c b/drivers/cpufreq/s3c24xx-cpufreq.c index bd340a1ca87da5..733aa5153e7451 100644 --- a/drivers/cpufreq/s3c24xx-cpufreq.c +++ b/drivers/cpufreq/s3c24xx-cpufreq.c @@ -144,11 +144,6 @@ static void s3c_cpufreq_setfvco(struct s3c_cpufreq_config *cfg) (cfg->info->set_fvco)(cfg); } -static inline void s3c_cpufreq_resume_clocks(void) -{ - cpu_cur.info->resume_clocks(); -} - static inline void s3c_cpufreq_updateclk(struct clk *clk, unsigned int freq) { @@ -417,9 +412,6 @@ static int s3c_cpufreq_resume(struct cpufreq_policy *policy) last_target = ~0; /* invalidate last_target setting */ - /* first, find out what speed we resumed at. */ - s3c_cpufreq_resume_clocks(); - /* whilst we will be called later on, we try and re-set the * cpu frequencies as soon as possible so that we do not end * up resuming devices and then immediately having to re-set From ee1693299c8b5cd9ad7f861606ea9bf20ef75717 Mon Sep 17 00:00:00 2001 From: Ross Lagerwall Date: Mon, 19 Jan 2015 13:19:38 +0000 Subject: [PATCH 039/788] xen/manage: Fix USB interaction issues when resuming commit 72978b2fe2f2cdf9f319c6c6dcdbe92b38de2be2 upstream. Commit 61a734d305e1 ("xen/manage: Always freeze/thaw processes when suspend/resuming") ensured that userspace processes were always frozen before suspending to reduce interaction issues when resuming devices. However, freeze_processes() does not freeze kernel threads. Freeze kernel threads as well to prevent deadlocks with the khubd thread when resuming devices. This is what native suspend and resume does. Example deadlock: [ 7279.648010] [] ? xen_poll_irq_timeout+0x3e/0x50 [ 7279.648010] [] xen_poll_irq+0x10/0x20 [ 7279.648010] [] xen_lock_spinning+0xb3/0x120 [ 7279.648010] [] __raw_callee_save_xen_lock_spinning+0x11/0x20 [ 7279.648010] [] ? usb_control_msg+0xe6/0x120 [ 7279.648010] [] ? _raw_spin_lock_irq+0x50/0x60 [ 7279.648010] [] wait_for_completion+0xac/0x160 [ 7279.648010] [] ? try_to_wake_up+0x2c0/0x2c0 [ 7279.648010] [] dpm_wait+0x32/0x40 [ 7279.648010] [] device_resume+0x90/0x210 [ 7279.648010] [] dpm_resume+0x121/0x250 [ 7279.648010] [] ? xenbus_dev_request_and_reply+0xc0/0xc0 [ 7279.648010] [] dpm_resume_end+0x15/0x30 [ 7279.648010] [] do_suspend+0x10a/0x200 [ 7279.648010] [] ? xen_pre_suspend+0x20/0x20 [ 7279.648010] [] shutdown_handler+0x120/0x150 [ 7279.648010] [] xenwatch_thread+0x9f/0x160 [ 7279.648010] [] ? finish_wait+0x80/0x80 [ 7279.648010] [] kthread+0xc9/0xe0 [ 7279.648010] [] ? flush_kthread_worker+0x80/0x80 [ 7279.648010] [] ret_from_fork+0x7c/0xb0 [ 7279.648010] [] ? flush_kthread_worker+0x80/0x80 [ 7441.216287] INFO: task khubd:89 blocked for more than 120 seconds. [ 7441.219457] Tainted: G X 3.13.11-ckt12.kz #1 [ 7441.222176] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. [ 7441.225827] khubd D ffff88003f433440 0 89 2 0x00000000 [ 7441.229258] ffff88003ceb9b98 0000000000000046 ffff88003ce83000 0000000000013440 [ 7441.232959] ffff88003ceb9fd8 0000000000013440 ffff88003cd13000 ffff88003ce83000 [ 7441.236658] 0000000000000286 ffff88003d3e0000 ffff88003ceb9bd0 00000001001aa01e [ 7441.240415] Call Trace: [ 7441.241614] [] schedule+0x29/0x70 [ 7441.243930] [] schedule_timeout+0x166/0x2c0 [ 7441.246681] [] ? call_timer_fn+0x110/0x110 [ 7441.249339] [] schedule_timeout_uninterruptible+0x1e/0x20 [ 7441.252644] [] msleep+0x20/0x30 [ 7441.254812] [] hub_port_reset+0xf0/0x580 [ 7441.257400] [] hub_port_init+0x75/0xb40 [ 7441.259981] [] ? update_autosuspend+0x39/0x60 [ 7441.262817] [] ? pm_runtime_set_autosuspend_delay+0x50/0xa0 [ 7441.266212] [] hub_thread+0x71a/0x1750 [ 7441.268728] [] ? finish_wait+0x80/0x80 [ 7441.271272] [] ? usb_port_resume+0x670/0x670 [ 7441.274067] [] kthread+0xc9/0xe0 [ 7441.276305] [] ? flush_kthread_worker+0x80/0x80 [ 7441.279131] [] ret_from_fork+0x7c/0xb0 [ 7441.281659] [] ? flush_kthread_worker+0x80/0x80 Signed-off-by: Ross Lagerwall Signed-off-by: David Vrabel Signed-off-by: Greg Kroah-Hartman --- drivers/xen/manage.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c index f8bb36f9d9cef8..bf1940706422fe 100644 --- a/drivers/xen/manage.c +++ b/drivers/xen/manage.c @@ -105,10 +105,16 @@ static void do_suspend(void) err = freeze_processes(); if (err) { - pr_err("%s: freeze failed %d\n", __func__, err); + pr_err("%s: freeze processes failed %d\n", __func__, err); goto out; } + err = freeze_kernel_threads(); + if (err) { + pr_err("%s: freeze kernel threads failed %d\n", __func__, err); + goto out_thaw; + } + err = dpm_suspend_start(PMSG_FREEZE); if (err) { pr_err("%s: dpm_suspend_start %d\n", __func__, err); From 2cb1c1c696853478fe83782e2771f4f07ec75ea1 Mon Sep 17 00:00:00 2001 From: Juergen Gross Date: Tue, 17 Feb 2015 08:02:47 +0100 Subject: [PATCH 040/788] xen-scsiback: mark pvscsi frontend request consumed only after last read commit facb5732b0bb59ebbc11b5d5abc249e677ddbeb6 upstream. A request in the ring buffer mustn't be read after it has been marked as consumed. Otherwise it might already have been reused by the frontend without violating the ring protocol. To avoid inconsistencies in the backend only work on a private copy of the request. This will ensure a malicious guest not being able to bypass consistency checks of the backend by modifying an active request. Signed-off-by: Juergen Gross Signed-off-by: David Vrabel Signed-off-by: Greg Kroah-Hartman --- drivers/xen/xen-scsiback.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/xen/xen-scsiback.c b/drivers/xen/xen-scsiback.c index e999496eda3efa..8e38499df77e78 100644 --- a/drivers/xen/xen-scsiback.c +++ b/drivers/xen/xen-scsiback.c @@ -708,12 +708,11 @@ static int prepare_pending_reqs(struct vscsibk_info *info, static int scsiback_do_cmd_fn(struct vscsibk_info *info) { struct vscsiif_back_ring *ring = &info->ring; - struct vscsiif_request *ring_req; + struct vscsiif_request ring_req; struct vscsibk_pend *pending_req; RING_IDX rc, rp; int err, more_to_do; uint32_t result; - uint8_t act; rc = ring->req_cons; rp = ring->sring->req_prod; @@ -734,11 +733,10 @@ static int scsiback_do_cmd_fn(struct vscsibk_info *info) if (!pending_req) return 1; - ring_req = RING_GET_REQUEST(ring, rc); + ring_req = *RING_GET_REQUEST(ring, rc); ring->req_cons = ++rc; - act = ring_req->act; - err = prepare_pending_reqs(info, ring_req, pending_req); + err = prepare_pending_reqs(info, &ring_req, pending_req); if (err) { switch (err) { case -ENODEV: @@ -754,9 +752,9 @@ static int scsiback_do_cmd_fn(struct vscsibk_info *info) return 1; } - switch (act) { + switch (ring_req.act) { case VSCSIIF_ACT_SCSI_CDB: - if (scsiback_gnttab_data_map(ring_req, pending_req)) { + if (scsiback_gnttab_data_map(&ring_req, pending_req)) { scsiback_fast_flush_area(pending_req); scsiback_do_resp_with_sense(NULL, DRIVER_ERROR << 24, 0, pending_req); @@ -767,7 +765,7 @@ static int scsiback_do_cmd_fn(struct vscsibk_info *info) break; case VSCSIIF_ACT_SCSI_ABORT: scsiback_device_action(pending_req, TMR_ABORT_TASK, - ring_req->ref_rqid); + ring_req.ref_rqid); break; case VSCSIIF_ACT_SCSI_RESET: scsiback_device_action(pending_req, TMR_LUN_RESET, 0); From 685e9ecfc4fc707d76a76d19602988815a2e9648 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 18 Feb 2015 13:50:16 +0200 Subject: [PATCH 041/788] ACPI / LPSS: Always disable I2C host controllers commit 3293c7b8ec213a640f5ea2e5efeaa2b7559b1e19 upstream. On Baytrail and Braswell the BIOS might leave the I2C host controllers enabled, probably because it uses them for its own purposes. This is fine in normal cases because the I2C driver will disable the hardware when it is probed anyway. However, in case of suspend to disk it is different story. If the driver happens to be compiled as a module the boot kernel never loads the driver thus leaving host controllers enabled upon loading the hibernation image. The I2C host controller interrupt mask register has default value of 0x8ff, in other words it has most of the interrupts unmasked. When combined with the fact that the host controller is enabled, the driver immediately starts getting interrupts even before its resume hook is called (once IO-APIC is resumed). Since the driver is not prepared for this it will crash the kernel due to NULL pointer derefence because dev->msgs is NULL. Unfortunately we were not able to get full backtrace to from the console which could be reproduced here. In order to fix this even when the driver is compiled as module, we disable the I2C host controllers in byt_i2c_setup() before devices are created. Reported-by: Yu Chen Signed-off-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/acpi_lpss.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index e75737fd7eefbc..64ccc3bb0826e0 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -105,6 +105,8 @@ static void lpss_uart_setup(struct lpss_private_data *pdata) } } +#define LPSS_I2C_ENABLE 0x6c + static void byt_i2c_setup(struct lpss_private_data *pdata) { unsigned int offset; @@ -117,6 +119,8 @@ static void byt_i2c_setup(struct lpss_private_data *pdata) if (readl(pdata->mmio_base + pdata->dev_desc->prv_offset)) pdata->fixed_clk_rate = 133000000; + + writel(0, pdata->mmio_base + LPSS_I2C_ENABLE); } static struct lpss_device_desc lpt_dev_desc = { From 077a83ddbc8f191038187e46165d8382b33f49b6 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 18 Feb 2015 13:50:17 +0200 Subject: [PATCH 042/788] ACPI / LPSS: Deassert resets for SPI host controllers on Braswell commit 3095794ae972bc6fc76af6cb3b864d6686b96094 upstream. On some Braswell systems BIOS leaves resets for SPI host controllers active. This prevents the SPI driver from transferring messages on wire. Fix this in similar way that we do for I2C already by deasserting resets for the SPI host controllers. Reported-by: Yang A Fang Signed-off-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/acpi_lpss.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 64ccc3bb0826e0..7d5880ded78a5f 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -105,9 +105,7 @@ static void lpss_uart_setup(struct lpss_private_data *pdata) } } -#define LPSS_I2C_ENABLE 0x6c - -static void byt_i2c_setup(struct lpss_private_data *pdata) +static void lpss_deassert_reset(struct lpss_private_data *pdata) { unsigned int offset; u32 val; @@ -116,6 +114,13 @@ static void byt_i2c_setup(struct lpss_private_data *pdata) val = readl(pdata->mmio_base + offset); val |= LPSS_RESETS_RESET_APB | LPSS_RESETS_RESET_FUNC; writel(val, pdata->mmio_base + offset); +} + +#define LPSS_I2C_ENABLE 0x6c + +static void byt_i2c_setup(struct lpss_private_data *pdata) +{ + lpss_deassert_reset(pdata); if (readl(pdata->mmio_base + pdata->dev_desc->prv_offset)) pdata->fixed_clk_rate = 133000000; @@ -170,6 +175,12 @@ static struct lpss_device_desc byt_i2c_dev_desc = { .setup = byt_i2c_setup, }; +static struct lpss_device_desc bsw_spi_dev_desc = { + .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX, + .prv_offset = 0x400, + .setup = lpss_deassert_reset, +}; + #else #define LPSS_ADDR(desc) (0UL) @@ -202,7 +213,7 @@ static const struct acpi_device_id acpi_lpss_device_ids[] = { /* Braswell LPSS devices */ { "80862288", LPSS_ADDR(byt_pwm_dev_desc) }, { "8086228A", LPSS_ADDR(byt_uart_dev_desc) }, - { "8086228E", LPSS_ADDR(byt_spi_dev_desc) }, + { "8086228E", LPSS_ADDR(bsw_spi_dev_desc) }, { "808622C1", LPSS_ADDR(byt_i2c_dev_desc) }, { "INT3430", LPSS_ADDR(lpt_dev_desc) }, From 121e0c0fbfd74f9136dd7784bd8c2e1deedad107 Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Fri, 2 Jan 2015 10:56:27 -0300 Subject: [PATCH 043/788] lmedm04: Increase Interupt due time to 200 msec commit cfcd7b825892cb498c6bcb13257f2141f7eacb76 upstream. Ocassionally the device fails to report back an interrupt urb status which results in false no lock trigger on the RS2000 demodulator. Increase time from 60 msecs to 200 msecs. Signed-off-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/dvb-usb-v2/lmedm04.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c index 994de53a574bf5..f1edb299c2f238 100644 --- a/drivers/media/usb/dvb-usb-v2/lmedm04.c +++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c @@ -344,9 +344,10 @@ static void lme2510_int_response(struct urb *lme_urb) usb_submit_urb(lme_urb, GFP_ATOMIC); - /* interrupt urb is due every 48 msecs while streaming - * add 12msecs for system lag */ - st->int_urb_due = jiffies + msecs_to_jiffies(60); + /* Interrupt urb is due every 48 msecs while streaming the buffer + * stores up to 4 periods if missed. Allow 200 msec for next interrupt. + */ + st->int_urb_due = jiffies + msecs_to_jiffies(200); } static int lme2510_int_read(struct dvb_usb_adapter *adap) From 36038d393c077550f2655ce38f2b84a0cb989432 Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Fri, 2 Jan 2015 10:56:28 -0300 Subject: [PATCH 044/788] lmedm04: Fix usb_submit_urb BOGUS urb xfer, pipe 1 != type 3 in interrupt urb commit 15e1ce33182d1d5dbd8efe8d382b9352dc857527 upstream. A quirk of some older firmwares that report endpoint pipe type as PIPE_BULK but the endpoint otheriwse functions as interrupt. Check if usb_endpoint_type is USB_ENDPOINT_XFER_BULK and set as usb_rcvbulkpipe. Signed-off-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/dvb-usb-v2/lmedm04.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c index f1edb299c2f238..15db9f67f4d125 100644 --- a/drivers/media/usb/dvb-usb-v2/lmedm04.c +++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c @@ -354,6 +354,7 @@ static int lme2510_int_read(struct dvb_usb_adapter *adap) { struct dvb_usb_device *d = adap_to_d(adap); struct lme2510_state *lme_int = adap_to_priv(adap); + struct usb_host_endpoint *ep; lme_int->lme_urb = usb_alloc_urb(0, GFP_ATOMIC); @@ -375,6 +376,12 @@ static int lme2510_int_read(struct dvb_usb_adapter *adap) adap, 8); + /* Quirk of pipe reporting PIPE_BULK but behaves as interrupt */ + ep = usb_pipe_endpoint(d->udev, lme_int->lme_urb->pipe); + + if (usb_endpoint_type(&ep->desc) == USB_ENDPOINT_XFER_BULK) + lme_int->lme_urb->pipe = usb_rcvbulkpipe(d->udev, 0xa), + lme_int->lme_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; usb_submit_urb(lme_int->lme_urb, GFP_ATOMIC); From ac6d7fe51218383b1fc583a1c0408b0cf6054957 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 25 Nov 2014 16:26:49 -0300 Subject: [PATCH 045/788] si2168: define symbol rate limits commit f1ecc5d119530fce01094307e029ed7f2c9067d8 upstream. w_scan complains about missing symbol rate limits: This dvb driver is *buggy*: the symbol rate limits are undefined - please report to linuxtv.org Chip supports 1 to 7.2 MSymbol/s on DVB-C. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/dvb-frontends/si2168.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index ce9ab442b4b67f..acf0fc31f78376 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -635,6 +635,8 @@ static const struct dvb_frontend_ops si2168_ops = { .delsys = {SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A}, .info = { .name = "Silicon Labs Si2168", + .symbol_rate_min = 1000000, + .symbol_rate_max = 7200000, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | From 58247aad2d2e36b82b75d9232bc7f89d103163c4 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 9 Feb 2015 16:51:40 +0300 Subject: [PATCH 046/788] ALSA: off by one bug in snd_riptide_joystick_probe() commit e4940626defdf6c92da1052ad3f12741c1a28c90 upstream. The problem here is that we check: if (dev >= SNDRV_CARDS) Then we increment "dev". if (!joystick_port[dev++]) Then we use it as an offset into a array with SNDRV_CARDS elements. if (!request_region(joystick_port[dev], 8, "Riptide gameport")) { This has 3 effects: 1) If you use the module option to specify the joystick port then it has to be shifted one space over. 2) The wrong error message will be printed on failure if you have over 32 cards. 3) Static checkers will correctly complain that are off by one. Fixes: db1005ec6ff8 ('ALSA: riptide - Fix joystick resource handling') Signed-off-by: Dan Carpenter Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/riptide/riptide.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index 6abc2ac8fffb3e..e7685727769532 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -2030,32 +2030,43 @@ snd_riptide_joystick_probe(struct pci_dev *pci, const struct pci_device_id *id) { static int dev; struct gameport *gameport; + int ret; if (dev >= SNDRV_CARDS) return -ENODEV; + if (!enable[dev]) { - dev++; - return -ENOENT; + ret = -ENOENT; + goto inc_dev; } - if (!joystick_port[dev++]) - return 0; + if (!joystick_port[dev]) { + ret = 0; + goto inc_dev; + } gameport = gameport_allocate_port(); - if (!gameport) - return -ENOMEM; + if (!gameport) { + ret = -ENOMEM; + goto inc_dev; + } if (!request_region(joystick_port[dev], 8, "Riptide gameport")) { snd_printk(KERN_WARNING "Riptide: cannot grab gameport 0x%x\n", joystick_port[dev]); gameport_free_port(gameport); - return -EBUSY; + ret = -EBUSY; + goto inc_dev; } gameport->io = joystick_port[dev]; gameport_register_port(gameport); pci_set_drvdata(pci, gameport); - return 0; + + ret = 0; +inc_dev: + dev++; + return ret; } static void snd_riptide_joystick_remove(struct pci_dev *pci) From a11711eaefc12a47593d6accdab2efb83da068eb Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 5 Feb 2015 12:23:33 +0100 Subject: [PATCH 047/788] ALSA: hda - Set up GPIO for Toshiba Satellite S50D commit 4227de2a7e5f0ff6a58e919a9c4f2bb06e882f48 upstream. Toshiba Satellite S50D laptop with an IDT codec uses the GPIO4 (0x10) as the master EAPD. Bugzilla: https://bugzilla.novell.com/show_bug.cgi?id=915858 Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_sigmatel.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 605d14003d257c..6d36c5b7880504 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -99,6 +99,7 @@ enum { STAC_HP_ENVY_BASS, STAC_HP_BNB13_EQ, STAC_HP_ENVY_TS_BASS, + STAC_92HD83XXX_GPIO10_EAPD, STAC_92HD83XXX_MODELS }; @@ -2141,6 +2142,19 @@ static void stac92hd83xxx_fixup_headset_jack(struct hda_codec *codec, spec->headset_jack = 1; } +static void stac92hd83xxx_fixup_gpio10_eapd(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = + spec->gpio_data = 0x10; + spec->eapd_switch = 0; +} + static const struct hda_verb hp_bnb13_eq_verbs[] = { /* 44.1KHz base */ { 0x22, 0x7A6, 0x3E }, @@ -2656,6 +2670,10 @@ static const struct hda_fixup stac92hd83xxx_fixups[] = { {} }, }, + [STAC_92HD83XXX_GPIO10_EAPD] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac92hd83xxx_fixup_gpio10_eapd, + }, }; static const struct hda_model_fixup stac92hd83xxx_models[] = { @@ -2861,6 +2879,8 @@ static const struct snd_pci_quirk stac92hd83xxx_fixup_tbl[] = { SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x148a, "HP Mini", STAC_92HD83XXX_HP_LED), SND_PCI_QUIRK_VENDOR(PCI_VENDOR_ID_HP, "HP", STAC_92HD83XXX_HP), + SND_PCI_QUIRK(PCI_VENDOR_ID_TOSHIBA, 0xfa91, + "Toshiba Satellite S50D", STAC_92HD83XXX_GPIO10_EAPD), {} /* terminator */ }; From 1e47ee3b890468a6419c5e14d41147f80621f5cf Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Fri, 13 Feb 2015 11:14:41 +0800 Subject: [PATCH 048/788] ALSA: hda - enable mute led quirk for one more hp machine. commit 7976eb49cbd138d8014fa02682d8f969ad1e9ff2 upstream. Otherwise, the mute led can't work at all. Tested-by: Taihsiang Ho BugLink: https://bugs.launchpad.net/bugs/1410704 Signed-off-by: Hui Wang Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 65f1f4e18ea5c5..0c993f7f9181e2 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4844,6 +4844,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x218b, "HP", ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED), /* ALC282 */ + SND_PCI_QUIRK(0x103c, 0x21f9, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x2210, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x2214, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x2236, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED), From 84eb4963dd68681c61237d8eadee7eb92425e6b1 Mon Sep 17 00:00:00 2001 From: Frank C Guenther Date: Tue, 17 Feb 2015 22:13:32 +0100 Subject: [PATCH 049/788] ALSA: usb: Fix support for Denon DA-300USB DAC (ID 154e:1003) commit 3cd1ce0420ce89937bef9096d5bdb13fbdf0f8b0 upstream. Fix problem where playback of Denon DA-300USB DAC sometimes does not start and leads to error messages like "clock source 41 is not valid, cannot use". Solution: Treat this device the same as other Denon/Marantz devices in sound/usb/quirks.c. Tested with both PCM and DSD formats. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=93261 Signed-off-by: Frank C Guenther Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/quirks.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index a7398412310bd5..a0795ba46ac5d7 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1122,6 +1122,7 @@ int snd_usb_select_mode_quirk(struct snd_usb_substream *subs, int err; switch (subs->stream->chip->usb_id) { + case USB_ID(0x154e, 0x1003): /* Denon DA-300USB */ case USB_ID(0x154e, 0x3005): /* Marantz HD-DAC1 */ case USB_ID(0x154e, 0x3006): /* Marantz SA-14S1 */ @@ -1201,6 +1202,7 @@ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe, (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) { switch (le16_to_cpu(dev->descriptor.idProduct)) { + case 0x1003: /* Denon DA300-USB */ case 0x3005: /* Marantz HD-DAC1 */ case 0x3006: /* Marantz SA-14S1 */ mdelay(20); @@ -1262,6 +1264,7 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, /* Denon/Marantz devices with USB DAC functionality */ switch (chip->usb_id) { + case USB_ID(0x154e, 0x1003): /* Denon DA300-USB */ case USB_ID(0x154e, 0x3005): /* Marantz HD-DAC1 */ case USB_ID(0x154e, 0x3006): /* Marantz SA-14S1 */ if (fp->altsetting == 2) From 23bd5cb4ea02ac1a402eddaa283f3d9001f040ca Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Tue, 10 Feb 2015 11:33:50 +0100 Subject: [PATCH 050/788] ALSA: hdspm - Constrain periods to 2 on older cards commit f0153c3d948c1764f6c920a0675d86fc1d75813e upstream. RME RayDAT and AIO use a fixed buffer size of 16384 samples. With period sizes of 32-4096, this translates to 4-512 periods. The older RME cards have a variable buffer size but require exactly two periods. This patch enforces nperiods=2 on those cards. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/rme9652/hdspm.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 3342705a571506..13bc2011d497b2 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -6086,6 +6086,9 @@ static int snd_hdspm_playback_open(struct snd_pcm_substream *substream) snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 64, 8192); + snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_PERIODS, + 2, 2); break; } @@ -6160,6 +6163,9 @@ static int snd_hdspm_capture_open(struct snd_pcm_substream *substream) snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 64, 8192); + snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_PERIODS, + 2, 2); break; } From 2e194fa73b28622a21268c24fcc8c9002a611318 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 27 Jan 2015 16:51:54 +0100 Subject: [PATCH 051/788] power_supply: 88pm860x: Fix leaked power supply on probe fail commit 24727b45b484e8937dcde53fa8d1aa70ac30ec0c upstream. Driver forgot to unregister power supply if request_threaded_irq() failed in probe(). In such case the memory associated with power supply leaked. Signed-off-by: Krzysztof Kozlowski Fixes: a830d28b48bf ("power_supply: Enable battery-charger for 88pm860x") Signed-off-by: Sebastian Reichel Signed-off-by: Greg Kroah-Hartman --- drivers/power/88pm860x_charger.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/power/88pm860x_charger.c b/drivers/power/88pm860x_charger.c index 650930e4fa798e..734ec4afa14d63 100644 --- a/drivers/power/88pm860x_charger.c +++ b/drivers/power/88pm860x_charger.c @@ -711,6 +711,7 @@ static int pm860x_charger_probe(struct platform_device *pdev) return 0; out_irq: + power_supply_unregister(&info->usb); while (--i >= 0) free_irq(info->irq[i], info); out: From 2529fba22f7de7ab10966aa71ad3fe8630ee93b5 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 09:51:48 +0100 Subject: [PATCH 052/788] power: bq24190: Fix ignored supplicants commit 478913fdbdfd4a781d91c993eb86838620fe7421 upstream. The driver mismatched 'num_supplicants' with 'num_supplies' of power_supply structure. It provided list of supplicants (power_supply.supplied_to) but did not set the number of supplicants. Instead it set the num_supplies which is used when iterating over number of supplies (power_supply.supplied_from). As a result the list of supplicants was ignored by core because its size was 0. Signed-off-by: Krzysztof Kozlowski Fixes: d7bf353fd0aa ("bq24190_charger: Add support for TI BQ24190 Battery Charger") Signed-off-by: Sebastian Reichel Signed-off-by: Greg Kroah-Hartman --- drivers/power/bq24190_charger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/bq24190_charger.c b/drivers/power/bq24190_charger.c index ad3ff8fbfbbb08..e4c95e1a6733e4 100644 --- a/drivers/power/bq24190_charger.c +++ b/drivers/power/bq24190_charger.c @@ -929,7 +929,7 @@ static void bq24190_charger_init(struct power_supply *charger) charger->properties = bq24190_charger_properties; charger->num_properties = ARRAY_SIZE(bq24190_charger_properties); charger->supplied_to = bq24190_charger_supplied_to; - charger->num_supplies = ARRAY_SIZE(bq24190_charger_supplied_to); + charger->num_supplicants = ARRAY_SIZE(bq24190_charger_supplied_to); charger->get_property = bq24190_charger_get_property; charger->set_property = bq24190_charger_set_property; charger->property_is_writeable = bq24190_charger_property_is_writeable; From 98b6ccca6c51861a336f687f45634512ec597d90 Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Thu, 15 Jan 2015 05:00:37 +0300 Subject: [PATCH 053/788] power: gpio-charger: balance enable/disable_irq_wake calls commit faeed51bb65ce0241052d8dc24ac331ade12e976 upstream. enable_irq_wakeup returns 0 in case it correctly enabled the IRQ to generate the wakeup event (and thus resume should call disable_irq_wake). Currently gpio-charger driver has this logic inverted. Correct that thus correcting enable/disable_irq_wake() calls balance. Signed-off-by: Dmitry Eremin-Solenikov Signed-off-by: Sebastian Reichel Signed-off-by: Greg Kroah-Hartman --- drivers/power/gpio-charger.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/power/gpio-charger.c b/drivers/power/gpio-charger.c index aef74bdf7ab316..b7424c8501f106 100644 --- a/drivers/power/gpio-charger.c +++ b/drivers/power/gpio-charger.c @@ -229,7 +229,7 @@ static int gpio_charger_suspend(struct device *dev) if (device_may_wakeup(dev)) gpio_charger->wakeup_enabled = - enable_irq_wake(gpio_charger->irq); + !enable_irq_wake(gpio_charger->irq); return 0; } @@ -239,7 +239,7 @@ static int gpio_charger_resume(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct gpio_charger *gpio_charger = platform_get_drvdata(pdev); - if (gpio_charger->wakeup_enabled) + if (device_may_wakeup(dev) && gpio_charger->wakeup_enabled) disable_irq_wake(gpio_charger->irq); power_supply_changed(&gpio_charger->charger); From 628de19983617462b1714218a66ff94f10cd2cda Mon Sep 17 00:00:00 2001 From: "Sumit.Saxena@avagotech.com" Date: Mon, 5 Jan 2015 20:05:58 +0530 Subject: [PATCH 054/788] megaraid_sas: endianness related bug fixes and code optimization commit 200aed582d6170a2687cd69095469b663f69f16f upstream. This patch addresses below issues: 1) Few endianness bug fixes. 2) Break the iteration after (MAX_LOGICAL_DRIVES_EXT - 1)), instead of MAX_LOGICAL_DRIVES_EXT. 3) Optimization in MFI INIT frame before firing. 4) MFI IO frame should be 256bytes aligned. Code is optimized to reduce the size of frame for fusion adapters and make the MFI frame size calculation a bit transparent and readable. Signed-off-by: Kashyap Desai Signed-off-by: Sumit Saxena Signed-off-by: Chaitra Basappa Reviewed-by: Martin K. Petersen Signed-off-by: Christoph Hellwig Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/megaraid/megaraid_sas_base.c | 24 ++++++++++----------- drivers/scsi/megaraid/megaraid_sas_fp.c | 14 ++++++------ drivers/scsi/megaraid/megaraid_sas_fusion.c | 7 +++--- drivers/scsi/megaraid/megaraid_sas_fusion.h | 9 ++------ 4 files changed, 24 insertions(+), 30 deletions(-) diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index ff283d23788ac0..401ed67cf4c137 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -3547,7 +3547,6 @@ static int megasas_create_frame_pool(struct megasas_instance *instance) int i; u32 max_cmd; u32 sge_sz; - u32 sgl_sz; u32 total_sz; u32 frame_count; struct megasas_cmd *cmd; @@ -3566,24 +3565,23 @@ static int megasas_create_frame_pool(struct megasas_instance *instance) } /* - * Calculated the number of 64byte frames required for SGL - */ - sgl_sz = sge_sz * instance->max_num_sge; - frame_count = (sgl_sz + MEGAMFI_FRAME_SIZE - 1) / MEGAMFI_FRAME_SIZE; - frame_count = 15; - - /* - * We need one extra frame for the MFI command + * For MFI controllers. + * max_num_sge = 60 + * max_sge_sz = 16 byte (sizeof megasas_sge_skinny) + * Total 960 byte (15 MFI frame of 64 byte) + * + * Fusion adapter require only 3 extra frame. + * max_num_sge = 16 (defined as MAX_IOCTL_SGE) + * max_sge_sz = 12 byte (sizeof megasas_sge64) + * Total 192 byte (3 MFI frame of 64 byte) */ - frame_count++; - + frame_count = instance->ctrl_context ? (3 + 1) : (15 + 1); total_sz = MEGAMFI_FRAME_SIZE * frame_count; /* * Use DMA pool facility provided by PCI layer */ instance->frame_dma_pool = pci_pool_create("megasas frame pool", - instance->pdev, total_sz, 64, - 0); + instance->pdev, total_sz, 256, 0); if (!instance->frame_dma_pool) { printk(KERN_DEBUG "megasas: failed to setup frame pool\n"); diff --git a/drivers/scsi/megaraid/megaraid_sas_fp.c b/drivers/scsi/megaraid/megaraid_sas_fp.c index 460c6a3d4aded1..7cae1c25c9a9c6 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fp.c +++ b/drivers/scsi/megaraid/megaraid_sas_fp.c @@ -172,6 +172,7 @@ void MR_PopulateDrvRaidMap(struct megasas_instance *instance) struct MR_FW_RAID_MAP_ALL *fw_map_old = NULL; struct MR_FW_RAID_MAP *pFwRaidMap = NULL; int i; + u16 ld_count; struct MR_DRV_RAID_MAP_ALL *drv_map = @@ -191,9 +192,10 @@ void MR_PopulateDrvRaidMap(struct megasas_instance *instance) fw_map_old = (struct MR_FW_RAID_MAP_ALL *) fusion->ld_map[(instance->map_id & 1)]; pFwRaidMap = &fw_map_old->raidMap; + ld_count = (u16)le32_to_cpu(pFwRaidMap->ldCount); #if VD_EXT_DEBUG - for (i = 0; i < le16_to_cpu(pFwRaidMap->ldCount); i++) { + for (i = 0; i < ld_count; i++) { dev_dbg(&instance->pdev->dev, "(%d) :Index 0x%x " "Target Id 0x%x Seq Num 0x%x Size 0/%llx\n", instance->unique_id, i, @@ -205,12 +207,12 @@ void MR_PopulateDrvRaidMap(struct megasas_instance *instance) memset(drv_map, 0, fusion->drv_map_sz); pDrvRaidMap->totalSize = pFwRaidMap->totalSize; - pDrvRaidMap->ldCount = (__le16)pFwRaidMap->ldCount; + pDrvRaidMap->ldCount = (__le16)cpu_to_le16(ld_count); pDrvRaidMap->fpPdIoTimeoutSec = pFwRaidMap->fpPdIoTimeoutSec; for (i = 0; i < MAX_RAIDMAP_LOGICAL_DRIVES + MAX_RAIDMAP_VIEWS; i++) pDrvRaidMap->ldTgtIdToLd[i] = (u8)pFwRaidMap->ldTgtIdToLd[i]; - for (i = 0; i < le16_to_cpu(pDrvRaidMap->ldCount); i++) { + for (i = 0; i < ld_count; i++) { pDrvRaidMap->ldSpanMap[i] = pFwRaidMap->ldSpanMap[i]; #if VD_EXT_DEBUG dev_dbg(&instance->pdev->dev, @@ -252,7 +254,7 @@ u8 MR_ValidateMapInfo(struct megasas_instance *instance) struct LD_LOAD_BALANCE_INFO *lbInfo; PLD_SPAN_INFO ldSpanInfo; struct MR_LD_RAID *raid; - int ldCount, num_lds; + u16 ldCount, num_lds; u16 ld; u32 expected_size; @@ -356,7 +358,7 @@ static int getSpanInfo(struct MR_DRV_RAID_MAP_ALL *map, for (ldCount = 0; ldCount < MAX_LOGICAL_DRIVES_EXT; ldCount++) { ld = MR_TargetIdToLdGet(ldCount, map); - if (ld >= MAX_LOGICAL_DRIVES_EXT) + if (ld >= (MAX_LOGICAL_DRIVES_EXT - 1)) continue; raid = MR_LdRaidGet(ld, map); dev_dbg(&instance->pdev->dev, "LD %x: span_depth=%x\n", @@ -1157,7 +1159,7 @@ void mr_update_span_set(struct MR_DRV_RAID_MAP_ALL *map, for (ldCount = 0; ldCount < MAX_LOGICAL_DRIVES_EXT; ldCount++) { ld = MR_TargetIdToLdGet(ldCount, map); - if (ld >= MAX_LOGICAL_DRIVES_EXT) + if (ld >= (MAX_LOGICAL_DRIVES_EXT - 1)) continue; raid = MR_LdRaidGet(ld, map); for (element = 0; element < MAX_QUAD_DEPTH; element++) { diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index 71557f64bb5e6e..b5362c19600029 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -698,12 +698,11 @@ megasas_ioc_init_fusion(struct megasas_instance *instance) cpu_to_le32(lower_32_bits(ioc_init_handle)); init_frame->data_xfer_len = cpu_to_le32(sizeof(struct MPI2_IOC_INIT_REQUEST)); - req_desc.Words = 0; + req_desc.u.low = cpu_to_le32(lower_32_bits(cmd->frame_phys_addr)); + req_desc.u.high = cpu_to_le32(upper_32_bits(cmd->frame_phys_addr)); req_desc.MFAIo.RequestFlags = (MEGASAS_REQ_DESCRIPT_FLAGS_MFA << - MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); - cpu_to_le32s((u32 *)&req_desc.MFAIo); - req_desc.Words |= cpu_to_le64(cmd->frame_phys_addr); + MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); /* * disable the intr before firing the init frame diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.h b/drivers/scsi/megaraid/megaraid_sas_fusion.h index 5ab7daee11be70..56e6db2d587427 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.h +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.h @@ -306,14 +306,9 @@ struct MPI2_RAID_SCSI_IO_REQUEST { * MPT RAID MFA IO Descriptor. */ struct MEGASAS_RAID_MFA_IO_REQUEST_DESCRIPTOR { -#if defined(__BIG_ENDIAN_BITFIELD) - u32 MessageAddress1:24; /* bits 31:8*/ - u32 RequestFlags:8; -#else u32 RequestFlags:8; - u32 MessageAddress1:24; /* bits 31:8*/ -#endif - u32 MessageAddress2; /* bits 61:32 */ + u32 MessageAddress1:24; + u32 MessageAddress2; }; /* Default Request Descriptor */ From a9d100ed9918346a16172dfb7940d739525d1c27 Mon Sep 17 00:00:00 2001 From: "Sumit.Saxena@avagotech.com" Date: Mon, 5 Jan 2015 20:06:08 +0530 Subject: [PATCH 055/788] megaraid_sas: fix the problem of non-existing VD exposed to host commit ab2f0608e16d64a23a2dcc8d83b966a0e0a281f3 upstream. This patch will address the issue of SCSI device created at OS level for non existing VD. ldTgtIdtoLd[] array has size 256 for Extended VD firmware and 128 for legacy firmware. Accessing indices beyond array size (OS will send TUR, INQUIRY.. commands upto device index 255), may return valid LD value and that particular SCSI command will be SUCCESS and creating SCSI device for non existing target(VD). For legacy firmware (64 VD firmware), invalidates LD (by setting LD value to 0xff) in LdTgtIdtoLd[] array for device index beyond 127, so that invalid LD(0xff) value should be returned beyond device index beyond 127. Signed-off-by: Kashyap Desai Signed-off-by: Sumit Saxena Reviewed-by: Martin K. Petersen Signed-off-by: Christoph Hellwig Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/megaraid/megaraid_sas_fp.c | 3 +++ drivers/scsi/megaraid/megaraid_sas_fusion.c | 14 ++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/megaraid/megaraid_sas_fp.c b/drivers/scsi/megaraid/megaraid_sas_fp.c index 7cae1c25c9a9c6..4f72287860eeea 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fp.c +++ b/drivers/scsi/megaraid/megaraid_sas_fp.c @@ -212,6 +212,9 @@ void MR_PopulateDrvRaidMap(struct megasas_instance *instance) for (i = 0; i < MAX_RAIDMAP_LOGICAL_DRIVES + MAX_RAIDMAP_VIEWS; i++) pDrvRaidMap->ldTgtIdToLd[i] = (u8)pFwRaidMap->ldTgtIdToLd[i]; + for (i = (MAX_RAIDMAP_LOGICAL_DRIVES + MAX_RAIDMAP_VIEWS); + i < MAX_LOGICAL_DRIVES_EXT; i++) + pDrvRaidMap->ldTgtIdToLd[i] = 0xff; for (i = 0; i < ld_count; i++) { pDrvRaidMap->ldSpanMap[i] = pFwRaidMap->ldSpanMap[i]; #if VD_EXT_DEBUG diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index b5362c19600029..3eecbaf9545c81 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -1716,9 +1716,19 @@ megasas_build_dcdb_fusion(struct megasas_instance *instance, if (scmd->device->channel < MEGASAS_MAX_PD_CHANNELS) goto NonFastPath; + /* + * For older firmware, Driver should not access ldTgtIdToLd + * beyond index 127 and for Extended VD firmware, ldTgtIdToLd + * should not go beyond 255. + */ + + if ((!fusion->fast_path_io) || + (device_id >= instance->fw_supported_vd_count)) + goto NonFastPath; + ld = MR_TargetIdToLdGet(device_id, local_map_ptr); - if ((ld >= instance->fw_supported_vd_count) || - (!fusion->fast_path_io)) + + if (ld >= instance->fw_supported_vd_count) goto NonFastPath; raid = MR_LdRaidGet(ld, local_map_ptr); From 3913acc965e871a7a657ac4f61ebcd1fb0d486ee Mon Sep 17 00:00:00 2001 From: "Sumit.Saxena@avagotech.com" Date: Mon, 5 Jan 2015 20:06:13 +0530 Subject: [PATCH 056/788] megaraid_sas: disable interrupt_mask before enabling hardware interrupts commit c2ced1719a1b903350955a511e1666e6d05a7f5b upstream. Update driver "mask_interrupts" before enable/disable hardware interrupt in order to avoid missing interrupts because of "mask_interrupts" still set to 1 and hardware interrupts are enabled. Signed-off-by: Sumit Saxena Signed-off-by: Chaitra Basappa Reviewed-by: Martin K. Petersen Signed-off-by: Christoph Hellwig Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/megaraid/megaraid_sas_fusion.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index 3eecbaf9545c81..69249faf268c03 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -103,6 +103,8 @@ megasas_enable_intr_fusion(struct megasas_instance *instance) { struct megasas_register_set __iomem *regs; regs = instance->reg_set; + + instance->mask_interrupts = 0; /* For Thunderbolt/Invader also clear intr on enable */ writel(~0, ®s->outbound_intr_status); readl(®s->outbound_intr_status); @@ -111,7 +113,6 @@ megasas_enable_intr_fusion(struct megasas_instance *instance) /* Dummy readl to force pci flush */ readl(®s->outbound_intr_mask); - instance->mask_interrupts = 0; } /** From 7a630886858a798f0519f55f0618702ead597d04 Mon Sep 17 00:00:00 2001 From: "Sumit.Saxena@avagotech.com" Date: Mon, 5 Jan 2015 20:06:18 +0530 Subject: [PATCH 057/788] megaraid_sas: complete outstanding IOCTLs before killing adapter commit c8dd61eff2780c481fcf919c1572e16e397c714e upstream. Driver calls megasas_complete_cmd() to call wake_up() for each MFI frame that was issued through the ioctl() interface prior to the kill adapter. This ensures userspace ioctl() system calls issued just before a kill adapter don't get stuck in wait state and IOCTLs are returned to the application. Signed-off-by: Sumit Saxena Signed-off-by: Chaitra Basappa Reviewed-by: Martin K. Petersen Signed-off-by: Christoph Hellwig Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/megaraid/megaraid_sas_base.c | 65 +++++++++++++++++---- drivers/scsi/megaraid/megaraid_sas_fusion.c | 4 -- 2 files changed, 54 insertions(+), 15 deletions(-) diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index 401ed67cf4c137..d63f04147a5952 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -1689,22 +1689,66 @@ static int megasas_slave_alloc(struct scsi_device *sdev) return 0; } +/* +* megasas_complete_outstanding_ioctls - Complete outstanding ioctls after a +* kill adapter +* @instance: Adapter soft state +* +*/ +void megasas_complete_outstanding_ioctls(struct megasas_instance *instance) +{ + int i; + struct megasas_cmd *cmd_mfi; + struct megasas_cmd_fusion *cmd_fusion; + struct fusion_context *fusion = instance->ctrl_context; + + /* Find all outstanding ioctls */ + if (fusion) { + for (i = 0; i < instance->max_fw_cmds; i++) { + cmd_fusion = fusion->cmd_list[i]; + if (cmd_fusion->sync_cmd_idx != (u32)ULONG_MAX) { + cmd_mfi = instance->cmd_list[cmd_fusion->sync_cmd_idx]; + if (cmd_mfi->sync_cmd && + cmd_mfi->frame->hdr.cmd != MFI_CMD_ABORT) + megasas_complete_cmd(instance, + cmd_mfi, DID_OK); + } + } + } else { + for (i = 0; i < instance->max_fw_cmds; i++) { + cmd_mfi = instance->cmd_list[i]; + if (cmd_mfi->sync_cmd && cmd_mfi->frame->hdr.cmd != + MFI_CMD_ABORT) + megasas_complete_cmd(instance, cmd_mfi, DID_OK); + } + } +} + + void megaraid_sas_kill_hba(struct megasas_instance *instance) { + /* Set critical error to block I/O & ioctls in case caller didn't */ + instance->adprecovery = MEGASAS_HW_CRITICAL_ERROR; + /* Wait 1 second to ensure IO or ioctls in build have posted */ + msleep(1000); if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_PLASMA) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY)) { - writel(MFI_STOP_ADP, &instance->reg_set->doorbell); + (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_PLASMA) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY)) { + writel(MFI_STOP_ADP, + &instance->reg_set->doorbell); /* Flush */ readl(&instance->reg_set->doorbell); if (instance->mpio && instance->requestorId) memset(instance->ld_ids, 0xff, MEGASAS_MAX_LD_IDS); } else { - writel(MFI_STOP_ADP, &instance->reg_set->inbound_doorbell); + writel(MFI_STOP_ADP, + &instance->reg_set->inbound_doorbell); } + /* Complete outstanding ioctls when adapter is killed */ + megasas_complete_outstanding_ioctls(instance); } /** @@ -3028,10 +3072,9 @@ megasas_issue_pending_cmds_again(struct megasas_instance *instance) "was tried multiple times during reset." "Shutting down the HBA\n", cmd, cmd->scmd, cmd->sync_cmd); + instance->instancet->disable_intr(instance); + atomic_set(&instance->fw_reset_no_pci_access, 1); megaraid_sas_kill_hba(instance); - - instance->adprecovery = - MEGASAS_HW_CRITICAL_ERROR; return; } } @@ -3165,8 +3208,8 @@ process_fw_state_change_wq(struct work_struct *work) if (megasas_transition_to_ready(instance, 1)) { printk(KERN_NOTICE "megaraid_sas:adapter not ready\n"); + atomic_set(&instance->fw_reset_no_pci_access, 1); megaraid_sas_kill_hba(instance); - instance->adprecovery = MEGASAS_HW_CRITICAL_ERROR; return ; } diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index 69249faf268c03..0764d20efb2652 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -2622,7 +2622,6 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int iotimeout) instance->host->host_no); megaraid_sas_kill_hba(instance); instance->skip_heartbeat_timer_del = 1; - instance->adprecovery = MEGASAS_HW_CRITICAL_ERROR; retval = FAILED; goto out; } @@ -2818,8 +2817,6 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int iotimeout) dev_info(&instance->pdev->dev, "Failed from %s %d\n", __func__, __LINE__); - instance->adprecovery = - MEGASAS_HW_CRITICAL_ERROR; megaraid_sas_kill_hba(instance); retval = FAILED; } @@ -2868,7 +2865,6 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int iotimeout) "adapter scsi%d.\n", instance->host->host_no); megaraid_sas_kill_hba(instance); instance->skip_heartbeat_timer_del = 1; - instance->adprecovery = MEGASAS_HW_CRITICAL_ERROR; retval = FAILED; } else { /* For VF: Restart HB timer if we didn't OCR */ From d265783440f16deddee8ac8e3c7b663fbcda3b8f Mon Sep 17 00:00:00 2001 From: James Hogan Date: Mon, 8 Dec 2014 13:17:07 -0300 Subject: [PATCH 058/788] rc-main: Re-apply filter for no-op protocol change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 983c5bd26b86ba1c0d79b770e596bb8b77e42f32 upstream. Since commit da6e162d6a46 ("[media] rc-core: simplify sysfs code"), when the IR protocol is set using the sysfs interface to the same set of protocols that are already set, store_protocols() does not refresh the scancode filter with the new protocol, even if it has already called the change_protocol() callback successfully. This results in the filter being disabled in the hardware and not re-enabled until the filter is set again using sysfs. Fix in store_protocols() by still re-applying the filter whenever the change_protocol() driver callback succeeded. The problem can be reproduced with the img-ir driver by setting a filter, and then setting the protocol to the same protocol that is already set: $ echo nec > protocols $ echo 0xffff > filter_mask $ echo nec > protocols After this, messages which don't match the filter were still being received. Fixes: da6e162d6a46 ("[media] rc-core: simplify sysfs code") Reported-by: Sifan Naeem Signed-off-by: James Hogan Cc: David Härdeman Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/rc/rc-main.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 86ffcd54339e14..f8c5e47a30aa70 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -1021,16 +1021,16 @@ static ssize_t store_protocols(struct device *device, goto out; } - if (new_protocols == old_protocols) { - rc = len; - goto out; + if (new_protocols != old_protocols) { + *current_protocols = new_protocols; + IR_dprintk(1, "Protocols changed to 0x%llx\n", + (long long)new_protocols); } - *current_protocols = new_protocols; - IR_dprintk(1, "Protocols changed to 0x%llx\n", (long long)new_protocols); - /* - * If the protocol is changed the filter needs updating. + * If a protocol change was attempted the filter may need updating, even + * if the actual protocol mask hasn't changed (since the driver may have + * cleared the filter). * Try setting the same filter with the new protocol (if any). * Fall back to clearing the filter. */ From f3764ff7405e079124ee817f630bfbb4f1f61c2f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 28 Jan 2015 18:17:41 -0300 Subject: [PATCH 059/788] timberdale: do not select TIMB_DMA commit 244829226f47ffb4d6009a2ccd2771cd149d8114 upstream. The timberdale media driver requires the use of the respective dma engine driver, but that may not be enabled, causing a Kconfig warning: warning: (VIDEO_TIMBERDALE) selects TIMB_DMA which has unmet direct dependencies (DMADEVICES && MFD_TIMBERDALE) This fixes the dependency by removing the inappropriate 'select' statement and replacing it with a direct dependency on the drivers that provide the services this needs. Fixes: 7155043c2d027 ("[media] enable COMPILE_TEST for media drivers") Signed-off-by: Arnd Bergmann Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/platform/Kconfig | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 765bffb49a7238..6a1334be75442b 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -56,10 +56,8 @@ config VIDEO_VIU config VIDEO_TIMBERDALE tristate "Support for timberdale Video In/LogiWIN" - depends on VIDEO_V4L2 && I2C && DMADEVICES - depends on MFD_TIMBERDALE || COMPILE_TEST - select DMA_ENGINE - select TIMB_DMA + depends on VIDEO_V4L2 && I2C + depends on (MFD_TIMBERDALE && TIMB_DMA) || COMPILE_TEST select VIDEO_ADV7180 select VIDEOBUF_DMA_CONTIG ---help--- From b211f750e0737bcef1ee672e9992ac5d9c322d36 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 20 Dec 2014 09:45:15 -0300 Subject: [PATCH 060/788] em28xx: fix em28xx-input removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit bbfebeea7640973613c484f0281bdd15d68fd873 upstream. Removing the em28xx-rc module results in the following lockdep splat, which is caused by trying to call cancel_delayed_work_sync() on an uninitialised delayed work. Fix this by ensuring we always initialise the work. INFO: trying to register non-static key. the code is fine but needs lockdep annotation. turning off the locking correctness validator. CPU: 0 PID: 2183 Comm: rmmod Not tainted 3.18.0+ #1464 Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree) Backtrace: [] (dump_backtrace) from [] (show_stack+0x18/0x1c) r6:c1419d2c r5:00000000 r4:00000000 r3:00000000 [] (show_stack) from [] (dump_stack+0x7c/0x98) [] (dump_stack) from [] (__lock_acquire+0x16d4/0x1bb0) r4:edf19f74 r3:df049380 [] (__lock_acquire) from [] (lock_acquire+0xb0/0x124) r10:00000000 r9:c003ba90 r8:00000000 r7:00000000 r6:00000000 r5:edf19f74 r4:00000000 [] (lock_acquire) from [] (flush_work+0x44/0x264) r10:00000000 r9:eaa86000 r8:edf190b0 r7:edf19f74 r6:00000001 r5:edf19f64 r4:00000000 [] (flush_work) from [] (__cancel_work_timer+0x8c/0x124) r7:00000000 r6:00000001 r5:00000000 r4:edf19f64 [] (__cancel_work_timer) from [] (cancel_delayed_work_sync+0x14/0x18) r7:00000000 r6:eccc3600 r5:00000000 r4:edf19000 [] (cancel_delayed_work_sync) from [] (em28xx_ir_fini+0x48/0xd8 [em28xx_rc]) [] (em28xx_ir_fini [em28xx_rc]) from [] (em28xx_unregister_extension+0x40/0x94 [em28xx]) r8:c000edc4 r7:00000081 r6:bf092bf4 r5:bf0b6a2c r4:edf19000 r3:bf0b5bc8 [] (em28xx_unregister_extension [em28xx]) from [] (em28xx_rc_unregister+0x14/0x1c [em28xx_rc]) r6:00000800 r5:00000000 r4:bf0b6a50 r3:bf0b64c8 [] (em28xx_rc_unregister [em28xx_rc]) from [] (SyS_delete_module+0x11c/0x180) [] (SyS_delete_module) from [] (ret_fast_syscall+0x0/0x48) r6:00000001 r5:beb0f813 r4:b8b17d00 Fixes: f52226099382 ("[media] em28xx: extend the support for device buttons") Signed-off-by: Russell King Reviewed-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/em28xx/em28xx-input.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index d8dc03aadfbd67..ef36c49ef16659 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -654,8 +654,6 @@ static void em28xx_init_buttons(struct em28xx *dev) if (dev->num_button_polling_addresses) { memset(dev->button_polling_last_values, 0, EM28XX_NUM_BUTTON_ADDRESSES_MAX); - INIT_DELAYED_WORK(&dev->buttons_query_work, - em28xx_query_buttons); schedule_delayed_work(&dev->buttons_query_work, msecs_to_jiffies(dev->button_polling_interval)); } @@ -689,6 +687,7 @@ static int em28xx_ir_init(struct em28xx *dev) } kref_get(&dev->ref); + INIT_DELAYED_WORK(&dev->buttons_query_work, em28xx_query_buttons); if (dev->board.buttons) em28xx_init_buttons(dev); From 9e1def0dee73ddbe672a103af68105a587583d06 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 20 Dec 2014 09:45:20 -0300 Subject: [PATCH 061/788] em28xx: ensure "closing" messages terminate with a newline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 0418ca6073478f54f1da2e4013fa50d36838de75 upstream. The lockdep splat addressed in a previous commit revealed that at least one message in em28xx-input.c was missing a new line: em28178 #0: Closing input extensionINFO: trying to register non-static key. Further inspection shows several other messages also miss a new line. These will be fixed in a subsequent patch. Fixes: aa929ad783c0 ("[media] em28xx: print a message at disconnect") Signed-off-by: Russell King Reviewed-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/em28xx/em28xx-audio.c | 2 +- drivers/media/usb/em28xx/em28xx-dvb.c | 2 +- drivers/media/usb/em28xx/em28xx-input.c | 2 +- drivers/media/usb/em28xx/em28xx-video.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 44ae1e0661e6ed..52dc9d70da7296 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -981,7 +981,7 @@ static int em28xx_audio_fini(struct em28xx *dev) return 0; } - em28xx_info("Closing audio extension"); + em28xx_info("Closing audio extension\n"); if (dev->adev.sndcard) { snd_card_disconnect(dev->adev.sndcard); diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 9877b699c6bccc..80c384c390e251 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -1724,7 +1724,7 @@ static int em28xx_dvb_fini(struct em28xx *dev) if (!dev->dvb) return 0; - em28xx_info("Closing DVB extension"); + em28xx_info("Closing DVB extension\n"); dvb = dev->dvb; client = dvb->i2c_client_tuner; diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index ef36c49ef16659..aea22deadc0a60 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -832,7 +832,7 @@ static int em28xx_ir_fini(struct em28xx *dev) return 0; } - em28xx_info("Closing input extension"); + em28xx_info("Closing input extension\n"); em28xx_shutdown_buttons(dev); diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index cf7f58b7629293..3b8c464bf25ab6 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1958,7 +1958,7 @@ static int em28xx_v4l2_fini(struct em28xx *dev) if (v4l2 == NULL) return 0; - em28xx_info("Closing video extension"); + em28xx_info("Closing video extension\n"); mutex_lock(&dev->lock); From 84795bdbfb3ccca0912ead78007aeaabfc504a63 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 20 Dec 2014 09:45:26 -0300 Subject: [PATCH 062/788] em28xx-input: fix missing newlines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit ebfd59cf549899a166d595bf1eab7eec3299ebe7 upstream. Inspection shows that newlines are missing from several kernel messages in em28xx-input. Fix these. Fixes: 5025076aadfe ("[media] em28xx-input: implement em28xx_ops: suspend/resume hooks") Signed-off-by: Russell King Reviewed-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/em28xx/em28xx-input.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index aea22deadc0a60..4007356d991df0 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -861,7 +861,7 @@ static int em28xx_ir_suspend(struct em28xx *dev) if (dev->is_audio_only) return 0; - em28xx_info("Suspending input extension"); + em28xx_info("Suspending input extension\n"); if (ir) cancel_delayed_work_sync(&ir->work); cancel_delayed_work_sync(&dev->buttons_query_work); @@ -878,7 +878,7 @@ static int em28xx_ir_resume(struct em28xx *dev) if (dev->is_audio_only) return 0; - em28xx_info("Resuming input extension"); + em28xx_info("Resuming input extension\n"); /* if suspend calls ir_raw_event_unregister(), the should call ir_raw_event_register() */ if (ir) From 274ccd1695b8c1c1daf3e8432388273ac791ab79 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 20 Dec 2014 09:45:36 -0300 Subject: [PATCH 063/788] em28xx-audio: fix missing newlines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 7818b0aab87b680fb10f68eccebeeb6cd8283c73 upstream. Inspection shows that newlines are missing from several kernel messages in em28xx-audio. Fix these. Fixes: 1b3fd2d34266 ("[media] em28xx-audio: don't hardcode audio URB calculus") Signed-off-by: Russell King Reviewed-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/em28xx/em28xx-audio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 52dc9d70da7296..82d58eb3d32a6c 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -820,7 +820,7 @@ static int em28xx_audio_urb_init(struct em28xx *dev) if (urb_size > ep_size * npackets) npackets = DIV_ROUND_UP(urb_size, ep_size); - em28xx_info("Number of URBs: %d, with %d packets and %d size", + em28xx_info("Number of URBs: %d, with %d packets and %d size\n", num_urb, npackets, urb_size); /* Estimate the bytes per period */ From 65a4de46a03b76aa782cb430de86d7f5f7825ede Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 20 Dec 2014 09:45:31 -0300 Subject: [PATCH 064/788] em28xx-core: fix missing newlines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 522adc7c1f70d302155bb07f7fdf5a7fe4ff9094 upstream. Inspection shows that newlines are missing from several kernel messages in em28xx-core. Fix these. Fixes: 9c669b731470 ("[media] em28xx: add suspend/resume to em28xx_ops") Signed-off-by: Russell King Reviewed-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/em28xx/em28xx-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 86461a708abe18..37456079f490d9 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -1125,7 +1125,7 @@ int em28xx_suspend_extension(struct em28xx *dev) { const struct em28xx_ops *ops = NULL; - em28xx_info("Suspending extensions"); + em28xx_info("Suspending extensions\n"); mutex_lock(&em28xx_devlist_mutex); list_for_each_entry(ops, &em28xx_extension_devlist, next) { if (ops->suspend) @@ -1139,7 +1139,7 @@ int em28xx_resume_extension(struct em28xx *dev) { const struct em28xx_ops *ops = NULL; - em28xx_info("Resuming extensions"); + em28xx_info("Resuming extensions\n"); mutex_lock(&em28xx_devlist_mutex); list_for_each_entry(ops, &em28xx_extension_devlist, next) { if (ops->resume) From 25222c5df5dd32b13a5a5e180472db952c2d19b2 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 20 Dec 2014 09:45:51 -0300 Subject: [PATCH 065/788] em28xx-video: fix missing newlines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 32e63f0368ed16e5ac417dc0bc2a5f8acbfb1511 upstream. Inspection shows that newlines are missing from several kernel messages in em28xx-video. Fix these. Fixes: a61f68119af3 ("[media] em28xx-video: implement em28xx_ops: suspend/resume hooks") Signed-off-by: Russell King Reviewed-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/em28xx/em28xx-video.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 3b8c464bf25ab6..f220c1f376e34b 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -2007,7 +2007,7 @@ static int em28xx_v4l2_suspend(struct em28xx *dev) if (!dev->has_video) return 0; - em28xx_info("Suspending video extension"); + em28xx_info("Suspending video extension\n"); em28xx_stop_urbs(dev); return 0; } @@ -2020,7 +2020,7 @@ static int em28xx_v4l2_resume(struct em28xx *dev) if (!dev->has_video) return 0; - em28xx_info("Resuming video extension"); + em28xx_info("Resuming video extension\n"); /* what do we do here */ return 0; } From cf9d9e04e0d97444b2585b47c239ab4c2833a173 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 20 Dec 2014 09:45:46 -0300 Subject: [PATCH 066/788] em28xx-dvb: fix missing newlines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit a084c57fc1ccd24ef8e6ca41e75afa745d5dbb98 upstream. Inspection shows that newlines are missing from several kernel messages in em28xx-dvb. Fix these. Fixes: ca2b46dacbf5 ("[media] em28xx-dvb: implement em28xx_ops: suspend/resume hooks") Signed-off-by: Russell King Reviewed-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/em28xx/em28xx-dvb.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 80c384c390e251..aee70d4832649e 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -1775,17 +1775,17 @@ static int em28xx_dvb_suspend(struct em28xx *dev) if (!dev->board.has_dvb) return 0; - em28xx_info("Suspending DVB extension"); + em28xx_info("Suspending DVB extension\n"); if (dev->dvb) { struct em28xx_dvb *dvb = dev->dvb; if (dvb->fe[0]) { ret = dvb_frontend_suspend(dvb->fe[0]); - em28xx_info("fe0 suspend %d", ret); + em28xx_info("fe0 suspend %d\n", ret); } if (dvb->fe[1]) { dvb_frontend_suspend(dvb->fe[1]); - em28xx_info("fe1 suspend %d", ret); + em28xx_info("fe1 suspend %d\n", ret); } } @@ -1802,18 +1802,18 @@ static int em28xx_dvb_resume(struct em28xx *dev) if (!dev->board.has_dvb) return 0; - em28xx_info("Resuming DVB extension"); + em28xx_info("Resuming DVB extension\n"); if (dev->dvb) { struct em28xx_dvb *dvb = dev->dvb; if (dvb->fe[0]) { ret = dvb_frontend_resume(dvb->fe[0]); - em28xx_info("fe0 resume %d", ret); + em28xx_info("fe0 resume %d\n", ret); } if (dvb->fe[1]) { ret = dvb_frontend_resume(dvb->fe[1]); - em28xx_info("fe1 resume %d", ret); + em28xx_info("fe1 resume %d\n", ret); } } From c45124791803fb535bbfe50abc49095bcf5ba962 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 20 Dec 2014 09:45:41 -0300 Subject: [PATCH 067/788] em28xx-audio: fix missing newlines, again MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit fbaa48d1853002c2e7bcf12c1fdc0f6fb16d1525 upstream. Inspection shows that newlines are missing from several kernel messages in em28xx-audio. Fix these. Fixes: 6d746f91f230 ("[media] em28xx-audio: implement em28xx_ops: suspend/resume hooks") Signed-off-by: Russell King Reviewed-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/em28xx/em28xx-audio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 82d58eb3d32a6c..49a5f9532bd8d8 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -1005,7 +1005,7 @@ static int em28xx_audio_suspend(struct em28xx *dev) if (dev->usb_audio_type != EM28XX_USB_AUDIO_VENDOR) return 0; - em28xx_info("Suspending audio extension"); + em28xx_info("Suspending audio extension\n"); em28xx_deinit_isoc_audio(dev); atomic_set(&dev->adev.stream_started, 0); return 0; @@ -1019,7 +1019,7 @@ static int em28xx_audio_resume(struct em28xx *dev) if (dev->usb_audio_type != EM28XX_USB_AUDIO_VENDOR) return 0; - em28xx_info("Resuming audio extension"); + em28xx_info("Resuming audio extension\n"); /* Nothing to do other than schedule_work() ?? */ schedule_work(&dev->adev.wq_trigger); return 0; From 611ef74966d545d0e56bcb8501ee610ec9383745 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Sun, 4 Jan 2015 23:15:47 +0800 Subject: [PATCH 068/788] mmc: sdhci-pxav3: fix unbalanced clock issues during probe commit 62cf983ad84275f8580c807e5e596216c46773cf upstream. Commit 0dcaa2499b7d ("sdhci-pxav3: Fix runtime PM initialization") tries to fix one hang issue caused by calling sdhci_add_host() on a suspended device. The fix enables the clock twice, once by clk_prepare_enable() and another by pm_runtime_get_sync(), meaning that the clock will never be gated at runtime PM suspend. I observed the power consumption regression on Marvell BG2Q SoCs. In fact, the fix is not correct. There still be a very small window during which a runtime suspend might somehow occur after pm_runtime_enable() but before pm_runtime_get_sync(). This patch fixes all of the two problems by just incrementing the usage counter before pm_runtime_enable(). It also adjust the order of disabling runtime pm and storing the usage count in the error path to handle clock gating properly. Signed-off-by: Jisheng Zhang Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-pxav3.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index ca3424e7ef717c..1255dd2b91c13a 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -365,10 +365,11 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) } } - pm_runtime_enable(&pdev->dev); - pm_runtime_get_sync(&pdev->dev); + pm_runtime_get_noresume(&pdev->dev); + pm_runtime_set_active(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, PXAV3_RPM_DELAY_MS); pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_enable(&pdev->dev); pm_suspend_ignore_children(&pdev->dev, 1); ret = sdhci_add_host(host); @@ -391,8 +392,8 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) return 0; err_add_host: - pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); err_of_parse: err_cd_req: err_mbus_win: From 20b85db94c1f4f3f86a83812b817d96fa37db61c Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Fri, 23 Jan 2015 18:08:21 +0800 Subject: [PATCH 069/788] mmc: sdhci-pxav3: fix race between runtime pm and irq commit 3bb10f60933e84abfe2be69f60b3486f9b96348b upstream. This patch is to fix a race condition that may cause an unhandled irq, which results in big sdhci interrupt numbers and endless "mmc1: got irq while runtime suspended" msgs before v3.15. Consider following scenario: CPU0 CPU1 sdhci_pxav3_runtime_suspend() spin_lock_irqsave(&host->lock, flags); sdhci_irq() spining on the &host->lock host->runtime_suspended = true; spin_unlock_irqrestore(&host->lock, flags); get the &host->lock runtime_suspended is true now return IRQ_NONE; Fix this race by using the core sdhci.c supplied sdhci_runtime_suspend_host() in runtime suspend hook which will disable card interrupts. We also use the sdhci_runtime_resume_host() in the runtime resume hook accordingly. Signed-off-by: Jisheng Zhang Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-pxav3.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index 1255dd2b91c13a..faf3b47166bcf3 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -458,11 +458,11 @@ static int sdhci_pxav3_runtime_suspend(struct device *dev) struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pxa *pxa = pltfm_host->priv; - unsigned long flags; + int ret; - spin_lock_irqsave(&host->lock, flags); - host->runtime_suspended = true; - spin_unlock_irqrestore(&host->lock, flags); + ret = sdhci_runtime_suspend_host(host); + if (ret) + return ret; clk_disable_unprepare(pxa->clk_io); if (!IS_ERR(pxa->clk_core)) @@ -476,17 +476,12 @@ static int sdhci_pxav3_runtime_resume(struct device *dev) struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pxa *pxa = pltfm_host->priv; - unsigned long flags; clk_prepare_enable(pxa->clk_io); if (!IS_ERR(pxa->clk_core)) clk_prepare_enable(pxa->clk_core); - spin_lock_irqsave(&host->lock, flags); - host->runtime_suspended = false; - spin_unlock_irqrestore(&host->lock, flags); - - return 0; + return sdhci_runtime_resume_host(host); } #endif From de4adf92f9722206f3547eff5eeaae8b632266c6 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Wed, 28 Jan 2015 19:54:12 +0800 Subject: [PATCH 070/788] mmc: sdhci-pxav3: fix setting of pdata->clk_delay_cycles commit 14460dbaf7a5a0488963fdb8232ad5c8a8cca7b7 upstream. Current code checks "clk_delay_cycles > 0" to know whether the optional "mrvl,clk_delay_cycles" is set or not. But of_property_read_u32() doesn't touch clk_delay_cycles if the property is not set. And type of clk_delay_cycles is u32, so we may always set pdata->clk_delay_cycles as a random value. This patch fix this problem by check the return value of of_property_read_u32() to know whether the optional clk-delay-cycles is set or not. Signed-off-by: Jisheng Zhang Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-pxav3.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index faf3b47166bcf3..2913bca3f94a5a 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -268,8 +268,8 @@ static struct sdhci_pxa_platdata *pxav3_get_mmc_pdata(struct device *dev) if (!pdata) return NULL; - of_property_read_u32(np, "mrvl,clk-delay-cycles", &clk_delay_cycles); - if (clk_delay_cycles > 0) + if (!of_property_read_u32(np, "mrvl,clk-delay-cycles", + &clk_delay_cycles)) pdata->clk_delay_cycles = clk_delay_cycles; return pdata; From 56073d467f5efa6a46975904d6eb4cd39a745b0b Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Thu, 29 Jan 2015 12:36:24 +0100 Subject: [PATCH 071/788] mmc: sdhci-pxav3: Fix SDR50 and DDR50 capabilities for the Armada 38x flavor commit d4b803c559843e3774736e5108cf6331cf75f64c upstream. According to erratum 'FE-2946959' both SDR50 and DDR50 modes require specific clock adjustments in SDIO3 Configuration register. However, this register was not part of the device tree binding. Even if the binding can (and will) be extended we still need handling the case where this register was not available. In this case we use the SDHCI_QUIRK_MISSING_CAPS quirk remove them from the capabilities. This commit is based on the work done by Marcin Wojtas Fixes: 5491ce3f79ee ("mmc: sdhci-pxav3: add support for the Armada 38x SDHCI controller") Signed-off-by: Gregory CLEMENT Signed-off-by: Marcin Wojtas Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-pxav3.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index 2913bca3f94a5a..8f340e9761488d 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -118,6 +118,20 @@ static int mv_conf_mbus_windows(struct platform_device *pdev, return 0; } +static int armada_38x_quirks(struct sdhci_host *host) +{ + host->quirks |= SDHCI_QUIRK_MISSING_CAPS; + /* + * According to erratum 'FE-2946959' both SDR50 and DDR50 + * modes require specific clock adjustments in SDIO3 + * Configuration register, if the adjustment is not done, + * remove them from the capabilities. + */ + host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1); + host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50); + return 0; +} + static void pxav3_reset(struct sdhci_host *host, u8 mask) { struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc)); @@ -319,6 +333,9 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) clk_prepare_enable(pxa->clk_core); if (of_device_is_compatible(np, "marvell,armada-380-sdhci")) { + ret = armada_38x_quirks(host); + if (ret < 0) + goto err_clk_get; ret = mv_conf_mbus_windows(pdev, mv_mbus_dram_info()); if (ret < 0) goto err_mbus_win; From 0c98e7c57536841d0e0d41c14136d9ba507deb9a Mon Sep 17 00:00:00 2001 From: Marcin Wojtas Date: Thu, 29 Jan 2015 12:36:25 +0100 Subject: [PATCH 072/788] mmc: sdhci-pxav3: Fix Armada 38x controller's caps according to erratum ERR-7878951 commit a39128bcd6f1e56c6514abf489b40b67d226093b upstream. According to erratum 'ERR-7878951' Armada 38x SDHCI controller has different capabilities than the ones shown in its registers: - it doesn't support the voltage switching: it can work either with 3.3V or 1.8V supply - it doesn't support the SDR104 mode - SDR50 mode doesn't need tuning The SDHCI_QUIRK_MISSING_CAPS quirk is used for updating the capabilities accordingly. [gregory.clement@free-electrons.com: port from 3.10] Fixes: 5491ce3f79ee ("mmc: sdhci-pxav3: add support for the Armada 38x SDHCI controller") Signed-off-by: Gregory CLEMENT Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-pxav3.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index 8f340e9761488d..e187f70b7c7053 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -118,8 +118,11 @@ static int mv_conf_mbus_windows(struct platform_device *pdev, return 0; } -static int armada_38x_quirks(struct sdhci_host *host) +static int armada_38x_quirks(struct platform_device *pdev, + struct sdhci_host *host) { + struct device_node *np = pdev->dev.of_node; + host->quirks |= SDHCI_QUIRK_MISSING_CAPS; /* * According to erratum 'FE-2946959' both SDR50 and DDR50 @@ -129,6 +132,21 @@ static int armada_38x_quirks(struct sdhci_host *host) */ host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1); host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50); + + /* + * According to erratum 'ERR-7878951' Armada 38x SDHCI + * controller has different capabilities than the ones shown + * in its registers + */ + host->caps = sdhci_readl(host, SDHCI_CAPABILITIES); + if (of_property_read_bool(np, "no-1-8-v")) { + host->caps &= ~SDHCI_CAN_VDD_180; + host->mmc->caps &= ~MMC_CAP_1_8V_DDR; + } else { + host->caps &= ~SDHCI_CAN_VDD_330; + } + host->caps1 &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_USE_SDR50_TUNING); + return 0; } @@ -332,8 +350,11 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) if (!IS_ERR(pxa->clk_core)) clk_prepare_enable(pxa->clk_core); + /* enable 1/8V DDR capable */ + host->mmc->caps |= MMC_CAP_1_8V_DDR; + if (of_device_is_compatible(np, "marvell,armada-380-sdhci")) { - ret = armada_38x_quirks(host); + ret = armada_38x_quirks(pdev, host); if (ret < 0) goto err_clk_get; ret = mv_conf_mbus_windows(pdev, mv_mbus_dram_info()); @@ -341,9 +362,6 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) goto err_mbus_win; } - /* enable 1/8V DDR capable */ - host->mmc->caps |= MMC_CAP_1_8V_DDR; - match = of_match_device(of_match_ptr(sdhci_pxav3_of_match), &pdev->dev); if (match) { ret = mmc_of_parse(host->mmc); From 66f6db714bd16874b5f9361c0dd879dd25c1fa04 Mon Sep 17 00:00:00 2001 From: Konstantin Khlebnikov Date: Wed, 11 Feb 2015 15:27:31 -0800 Subject: [PATCH 073/788] proc/pagemap: walk page tables under pte lock commit 05fbf357d94152171bc50f8a369390f1f16efd89 upstream. Lockless access to pte in pagemap_pte_range() might race with page migration and trigger BUG_ON(!PageLocked()) in migration_entry_to_page(): CPU A (pagemap) CPU B (migration) lock_page() try_to_unmap(page, TTU_MIGRATION...) make_migration_entry() set_pte_at() pte_to_pagemap_entry() remove_migration_ptes() unlock_page() if(is_migration_entry()) migration_entry_to_page() BUG_ON(!PageLocked(page)) Also lockless read might be non-atomic if pte is larger than wordsize. Other pte walkers (smaps, numa_maps, clear_refs) already lock ptes. Fixes: 052fb0d635df ("proc: report file/anon bit in /proc/pid/pagemap") Signed-off-by: Konstantin Khlebnikov Reported-by: Andrey Ryabinin Reviewed-by: Cyrill Gorcunov Acked-by: Naoya Horiguchi Acked-by: Kirill A. Shutemov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/proc/task_mmu.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 246eae84b13b1b..88f9b8352742c7 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -1069,7 +1069,7 @@ static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, struct vm_area_struct *vma; struct pagemapread *pm = walk->private; spinlock_t *ptl; - pte_t *pte; + pte_t *pte, *orig_pte; int err = 0; /* find the first VMA at or above 'addr' */ @@ -1130,15 +1130,19 @@ static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, BUG_ON(is_vm_hugetlb_page(vma)); /* Addresses in the VMA. */ - for (; addr < min(end, vma->vm_end); addr += PAGE_SIZE) { + orig_pte = pte = pte_offset_map_lock(walk->mm, pmd, addr, &ptl); + for (; addr < min(end, vma->vm_end); pte++, addr += PAGE_SIZE) { pagemap_entry_t pme; - pte = pte_offset_map(pmd, addr); + pte_to_pagemap_entry(&pme, pm, vma, addr, *pte); - pte_unmap(pte); err = add_to_pagemap(addr, &pme, pm); if (err) - return err; + break; } + pte_unmap_unlock(orig_pte, ptl); + + if (err) + return err; if (addr == end) break; From 0c0f2544c2355899dcf62821ed0b230b60b79c22 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 14 Jan 2015 13:08:57 -0500 Subject: [PATCH 074/788] nfs: don't call blocking operations while !TASK_RUNNING commit 6ffa30d3f734d4f6b478081dfc09592021028f90 upstream. Bruce reported seeing this warning pop when mounting using v4.1: ------------[ cut here ]------------ WARNING: CPU: 1 PID: 1121 at kernel/sched/core.c:7300 __might_sleep+0xbd/0xd0() do not call blocking ops when !TASK_RUNNING; state=1 set at [] prepare_to_wait+0x2f/0x90 Modules linked in: rpcsec_gss_krb5 auth_rpcgss nfsv4 dns_resolver nfs lockd grace sunrpc fscache ip6t_rpfilter ip6t_REJECT nf_reject_ipv6 xt_conntrack ebtable_nat ebtable_broute bridge stp llc ebtable_filter ebtables ip6table_nat nf_conntrack_ipv6 nf_defrag_ipv6 nf_nat_ipv6 ip6table_mangle ip6table_security ip6table_raw ip6table_filter ip6_tables iptable_nat nf_conntrack_ipv4 nf_defrag_ipv4 nf_nat_ipv4 nf_nat nf_conntrack iptable_mangle iptable_security iptable_raw snd_hda_codec_generic snd_hda_intel snd_hda_controller snd_hda_codec snd_hwdep snd_pcm snd_timer ppdev joydev snd virtio_console virtio_balloon pcspkr serio_raw parport_pc parport pvpanic floppy soundcore i2c_piix4 virtio_blk virtio_net qxl drm_kms_helper ttm drm virtio_pci virtio_ring ata_generic virtio pata_acpi CPU: 1 PID: 1121 Comm: nfsv4.1-svc Not tainted 3.19.0-rc4+ #25 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.7.5-20140709_153950- 04/01/2014 0000000000000000 000000004e5e3f73 ffff8800b998fb48 ffffffff8186ac78 0000000000000000 ffff8800b998fba0 ffff8800b998fb88 ffffffff810ac9da ffff8800b998fb68 ffffffff81c923e7 00000000000004d9 0000000000000000 Call Trace: [] dump_stack+0x4c/0x65 [] warn_slowpath_common+0x8a/0xc0 [] warn_slowpath_fmt+0x55/0x70 [] ? prepare_to_wait+0x2f/0x90 [] ? prepare_to_wait+0x2f/0x90 [] __might_sleep+0xbd/0xd0 [] kmem_cache_alloc_trace+0x243/0x430 [] ? groups_alloc+0x3e/0x130 [] groups_alloc+0x3e/0x130 [] svcauth_unix_accept+0x16e/0x290 [sunrpc] [] svc_authenticate+0xe1/0xf0 [sunrpc] [] svc_process_common+0x244/0x6a0 [sunrpc] [] bc_svc_process+0x1c4/0x260 [sunrpc] [] nfs41_callback_svc+0x128/0x1f0 [nfsv4] [] ? wait_woken+0xc0/0xc0 [] ? nfs4_callback_svc+0x60/0x60 [nfsv4] [] kthread+0x11f/0x140 [] ? local_clock+0x15/0x30 [] ? kthread_create_on_node+0x250/0x250 [] ret_from_fork+0x7c/0xb0 [] ? kthread_create_on_node+0x250/0x250 ---[ end trace 675220a11e30f4f2 ]--- nfs41_callback_svc does most of its work while in TASK_INTERRUPTIBLE, which is just wrong. Fix that by finishing the wait immediately if we've found that the list has something on it. Also, we don't expect this kthread to accept signals, so we should be using a TASK_UNINTERRUPTIBLE sleep instead. That however, opens us up hung task warnings from the watchdog, so have the schedule_timeout wake up every 60s if there's no callback activity. Reported-by: "J. Bruce Fields" Signed-off-by: Jeff Layton Signed-off-by: Trond Myklebust Signed-off-by: Greg Kroah-Hartman --- fs/nfs/callback.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index b8fb3a4ef64955..351be9205bf889 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -128,22 +128,24 @@ nfs41_callback_svc(void *vrqstp) if (try_to_freeze()) continue; - prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_INTERRUPTIBLE); + prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_UNINTERRUPTIBLE); spin_lock_bh(&serv->sv_cb_lock); if (!list_empty(&serv->sv_cb_list)) { req = list_first_entry(&serv->sv_cb_list, struct rpc_rqst, rq_bc_list); list_del(&req->rq_bc_list); spin_unlock_bh(&serv->sv_cb_lock); + finish_wait(&serv->sv_cb_waitq, &wq); dprintk("Invoking bc_svc_process()\n"); error = bc_svc_process(serv, req, rqstp); dprintk("bc_svc_process() returned w/ error code= %d\n", error); } else { spin_unlock_bh(&serv->sv_cb_lock); - schedule(); + /* schedule_timeout to game the hung task watchdog */ + schedule_timeout(60 * HZ); + finish_wait(&serv->sv_cb_waitq, &wq); } - finish_wait(&serv->sv_cb_waitq, &wq); } return 0; } From 5fb43ea907ecf14959ea639dfc4d9fbc9125d8ae Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 13 Feb 2015 21:03:16 -0500 Subject: [PATCH 075/788] NFS: struct nfs_commit_info.lock must always point to inode->i_lock commit f4086a3d789dbe18949862276d83b8f49fce6d2f upstream. Commit 411a99adffb4f (nfs: clear_request_commit while holding i_lock) assumes that the nfs_commit_info always points to the inode->i_lock. For historical reasons, that is not the case for O_DIRECT writes. Cc: Weston Andros Adamson Fixes: 411a99adffb4f ("nfs: clear_request_commit while holding i_lock") Signed-off-by: Trond Myklebust Signed-off-by: Greg Kroah-Hartman --- fs/nfs/direct.c | 2 +- include/linux/nfs_xdr.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 294692ff83b1a4..a094b0c34ac363 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -242,7 +242,7 @@ static void nfs_direct_release_pages(struct page **pages, unsigned int npages) void nfs_init_cinfo_from_dreq(struct nfs_commit_info *cinfo, struct nfs_direct_req *dreq) { - cinfo->lock = &dreq->lock; + cinfo->lock = &dreq->inode->i_lock; cinfo->mds = &dreq->mds_cinfo; cinfo->ds = &dreq->ds_cinfo; cinfo->dreq = dreq; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 467c84efb5960f..06ebfa1f874b0e 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1342,7 +1342,7 @@ struct nfs_commit_completion_ops { }; struct nfs_commit_info { - spinlock_t *lock; + spinlock_t *lock; /* inode->i_lock */ struct nfs_mds_commit_info *mds; struct pnfs_ds_commit_info *ds; struct nfs_direct_req *dreq; /* O_DIRECT request */ From 32a699154df8fa9aaeb8f4a0de20a897dc0ec065 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Wed, 4 Feb 2015 10:52:03 +0000 Subject: [PATCH 076/788] KVM: MIPS: Disable HTW while in guest commit c4c6f2cad9e1d4cc076bc183c3689cc9e7019c75 upstream. Ensure any hardware page table walker (HTW) is disabled while in KVM guest mode, as KVM doesn't yet set up hardware page table walking for guest mappings so the wrong mappings would get loaded, resulting in the guest hanging or crashing once it reaches userland. The HTW is disabled and re-enabled around the call to __kvm_mips_vcpu_run() which does the initial switch into guest mode and the final switch out of guest context. Additionally it is enabled for the duration of guest exits (i.e. kvm_mips_handle_exit()), getting disabled again before returning back to guest or host. In all cases the HTW is only disabled in normal kernel mode while interrupts are disabled, so that the HTW doesn't get left disabled if the process is preempted. Signed-off-by: James Hogan Cc: Paolo Bonzini Cc: Ralf Baechle Cc: Markos Chandras Cc: Gleb Natapov Cc: kvm@vger.kernel.org Cc: linux-mips@linux-mips.org Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/mips/kvm/mips.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/arch/mips/kvm/mips.c b/arch/mips/kvm/mips.c index e3b21e51ff7e92..dd133ccecec41e 100644 --- a/arch/mips/kvm/mips.c +++ b/arch/mips/kvm/mips.c @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -385,8 +386,14 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) kvm_guest_enter(); + /* Disable hardware page table walking while in guest */ + htw_stop(); + r = __kvm_mips_vcpu_run(run, vcpu); + /* Re-enable HTW before enabling interrupts */ + htw_start(); + kvm_guest_exit(); local_irq_enable(); @@ -1002,6 +1009,9 @@ int kvm_mips_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu) enum emulation_result er = EMULATE_DONE; int ret = RESUME_GUEST; + /* re-enable HTW before enabling interrupts */ + htw_start(); + /* Set a default exit reason */ run->exit_reason = KVM_EXIT_UNKNOWN; run->ready_for_interrupt_injection = 1; @@ -1136,6 +1146,9 @@ int kvm_mips_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu) } } + /* Disable HTW before returning to guest or host */ + htw_stop(); + return ret; } From 1835ecd56ddb5b5de5f17aaa60cd68edc83eae81 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Wed, 4 Feb 2015 17:06:37 +0000 Subject: [PATCH 077/788] KVM: MIPS: Don't leak FPU/DSP to guest commit f798217dfd038af981a18bbe4bc57027a08bb182 upstream. The FPU and DSP are enabled via the CP0 Status CU1 and MX bits by kvm_mips_set_c0_status() on a guest exit, presumably in case there is active state that needs saving if pre-emption occurs. However neither of these bits are cleared again when returning to the guest. This effectively gives the guest access to the FPU/DSP hardware after the first guest exit even though it is not aware of its presence, allowing FP instructions in guest user code to intermittently actually execute instead of trapping into the guest OS for emulation. It will then read & manipulate the hardware FP registers which technically belong to the user process (e.g. QEMU), or are stale from another user process. It can also crash the guest OS by causing an FP exception, for which a guest exception handler won't have been registered. First lets save and disable the FPU (and MSA) state with lose_fpu(1) before entering the guest. This simplifies the problem, especially for when guest FPU/MSA support is added in the future, and prevents FR=1 FPU state being live when the FR bit gets cleared for the guest, which according to the architecture causes the contents of the FPU and vector registers to become UNPREDICTABLE. We can then safely remove the enabling of the FPU in kvm_mips_set_c0_status(), since there should never be any active FPU or MSA state to save at pre-emption, which should plug the FPU leak. DSP state is always live rather than being lazily restored, so for that it is simpler to just clear the MX bit again when re-entering the guest. Signed-off-by: James Hogan Cc: Paolo Bonzini Cc: Ralf Baechle Cc: Sanjay Lal Cc: Gleb Natapov Cc: kvm@vger.kernel.org Cc: linux-mips@linux-mips.org Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/mips/kvm/locore.S | 2 +- arch/mips/kvm/mips.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/mips/kvm/locore.S b/arch/mips/kvm/locore.S index d7279c03c517a0..4a68b176d6e4f8 100644 --- a/arch/mips/kvm/locore.S +++ b/arch/mips/kvm/locore.S @@ -434,7 +434,7 @@ __kvm_mips_return_to_guest: /* Setup status register for running guest in UM */ .set at or v1, v1, (ST0_EXL | KSU_USER | ST0_IE) - and v1, v1, ~ST0_CU0 + and v1, v1, ~(ST0_CU0 | ST0_MX) .set noat mtc0 v1, CP0_STATUS ehb diff --git a/arch/mips/kvm/mips.c b/arch/mips/kvm/mips.c index dd133ccecec41e..270bbd41769e6a 100644 --- a/arch/mips/kvm/mips.c +++ b/arch/mips/kvm/mips.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -379,6 +380,8 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) vcpu->mmio_needed = 0; } + lose_fpu(1); + local_irq_disable(); /* Check if we have any exceptions/interrupts pending */ kvm_mips_deliver_interrupts(vcpu, @@ -987,9 +990,6 @@ static void kvm_mips_set_c0_status(void) { uint32_t status = read_c0_status(); - if (cpu_has_fpu) - status |= (ST0_CU1); - if (cpu_has_dsp) status |= (ST0_MX); From c1f002d6b8ec0dd1d8268d4ec397dc4e10889ec4 Mon Sep 17 00:00:00 2001 From: Manuel Lauss Date: Wed, 18 Feb 2015 11:01:56 +0100 Subject: [PATCH 078/788] MIPS: Alchemy: Fix cpu clock calculation commit 69e4e63ec816a7e22cc3aa14bc7ef4ac734d370c upstream. The current code uses bits 0-6 of the sys_cpupll register to calculate core clock speed. However this is only valid on Au1300, on all earlier models the hardware only uses bits 0-5 to generate core clock. This fixes clock calculation on the MTX1 (Au1500), where bit 6 of cpupll is set as well, which ultimately lead the code to calculate a bogus cpu core clock and also uart base clock down the line. Signed-off-by: Manuel Lauss Reported-by: John Crispin Tested-by: Bruno Randolf Cc: Linux-MIPS Patchwork: https://patchwork.linux-mips.org/patch/9279/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/alchemy/common/clock.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/mips/alchemy/common/clock.c b/arch/mips/alchemy/common/clock.c index 48a9dfc55b51aa..c4d21ceae51662 100644 --- a/arch/mips/alchemy/common/clock.c +++ b/arch/mips/alchemy/common/clock.c @@ -127,6 +127,8 @@ static unsigned long alchemy_clk_cpu_recalc(struct clk_hw *hw, t = 396000000; else { t = alchemy_rdsys(AU1000_SYS_CPUPLL) & 0x7f; + if (alchemy_get_cputype() < ALCHEMY_CPU_AU1300) + t &= 0x3f; t *= parent_rate; } From 905fd8504005c2cc6a712a4b052d76ca0814f544 Mon Sep 17 00:00:00 2001 From: Markos Chandras Date: Mon, 24 Nov 2014 14:40:11 +0000 Subject: [PATCH 079/788] MIPS: kernel: cps-vec: Replace "addi" with "addiu" commit acac4108df6029c03195513ead7073bbb0cb9718 upstream. The "addi" instruction will trap on overflows which is not something we need in this code, so we replace that with "addiu". Link: http://www.linux-mips.org/archives/linux-mips/2015-01/msg00430.html Cc: Maciej W. Rozycki Cc: Paul Burton Signed-off-by: Markos Chandras Signed-off-by: Greg Kroah-Hartman --- arch/mips/kernel/cps-vec.S | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/arch/mips/kernel/cps-vec.S b/arch/mips/kernel/cps-vec.S index 0384b05ab5a024..55b759a0019e61 100644 --- a/arch/mips/kernel/cps-vec.S +++ b/arch/mips/kernel/cps-vec.S @@ -99,11 +99,11 @@ not_nmi: xori t2, t1, 0x7 beqz t2, 1f li t3, 32 - addi t1, t1, 1 + addiu t1, t1, 1 sllv t1, t3, t1 1: /* At this point t1 == I-cache sets per way */ _EXT t2, v0, MIPS_CONF1_IA_SHF, MIPS_CONF1_IA_SZ - addi t2, t2, 1 + addiu t2, t2, 1 mul t1, t1, t0 mul t1, t1, t2 @@ -126,11 +126,11 @@ icache_done: xori t2, t1, 0x7 beqz t2, 1f li t3, 32 - addi t1, t1, 1 + addiu t1, t1, 1 sllv t1, t3, t1 1: /* At this point t1 == D-cache sets per way */ _EXT t2, v0, MIPS_CONF1_DA_SHF, MIPS_CONF1_DA_SZ - addi t2, t2, 1 + addiu t2, t2, 1 mul t1, t1, t0 mul t1, t1, t2 @@ -250,7 +250,7 @@ LEAF(mips_cps_core_init) mfc0 t0, CP0_MVPCONF0 srl t0, t0, MVPCONF0_PVPE_SHIFT andi t0, t0, (MVPCONF0_PVPE >> MVPCONF0_PVPE_SHIFT) - addi t7, t0, 1 + addiu t7, t0, 1 /* If there's only 1, we're done */ beqz t0, 2f @@ -280,7 +280,7 @@ LEAF(mips_cps_core_init) mttc0 t0, CP0_TCHALT /* Next VPE */ - addi t5, t5, 1 + addiu t5, t5, 1 slt t0, t5, t7 bnez t0, 1b nop @@ -317,7 +317,7 @@ LEAF(mips_cps_boot_vpes) mfc0 t1, CP0_MVPCONF0 srl t1, t1, MVPCONF0_PVPE_SHIFT andi t1, t1, MVPCONF0_PVPE >> MVPCONF0_PVPE_SHIFT - addi t1, t1, 1 + addiu t1, t1, 1 /* Calculate a mask for the VPE ID from EBase.CPUNum */ clz t1, t1 @@ -424,7 +424,7 @@ LEAF(mips_cps_boot_vpes) /* Next VPE */ 2: srl t6, t6, 1 - addi t5, t5, 1 + addiu t5, t5, 1 bnez t6, 1b nop From e800e504c89f3ace417e0b94aad63ded14da8d01 Mon Sep 17 00:00:00 2001 From: Markos Chandras Date: Wed, 5 Nov 2014 14:17:52 +0000 Subject: [PATCH 080/788] MIPS: asm: asmmacro: Replace "add" instructions with "addu" commit 98a833c1fa4de0695830f77b2d13fd86693da298 upstream. The "add" instruction is actually a macro in binutils and depending on the size of the immediate it can expand to an "addi" instruction. However, the "addi" instruction traps on overflows which is not something we want on address calculation. Link: http://www.linux-mips.org/archives/linux-mips/2015-01/msg00121.html Cc: Paul Burton Cc: Maciej W. Rozycki Signed-off-by: Markos Chandras Signed-off-by: Greg Kroah-Hartman --- arch/mips/include/asm/asmmacro.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/mips/include/asm/asmmacro.h b/arch/mips/include/asm/asmmacro.h index 6caf8766b80f16..71fef0af9c9abb 100644 --- a/arch/mips/include/asm/asmmacro.h +++ b/arch/mips/include/asm/asmmacro.h @@ -304,7 +304,7 @@ .set push .set noat SET_HARDFLOAT - add $1, \base, \off + addu $1, \base, \off .word LDD_MSA_INSN | (\wd << 6) .set pop .endm @@ -313,7 +313,7 @@ .set push .set noat SET_HARDFLOAT - add $1, \base, \off + addu $1, \base, \off .word STD_MSA_INSN | (\wd << 6) .set pop .endm From de0a5f5e9a8df30378225c85aa4a3e4e254a0299 Mon Sep 17 00:00:00 2001 From: Markos Chandras Date: Mon, 26 Jan 2015 09:40:34 +0000 Subject: [PATCH 081/788] MIPS: asm: pgtable: Add c0 hazards on HTW start/stop sequences commit 461d1597ffad7a826f8aaa63ab0727c37b632e34 upstream. When we use htw_{start,stop}() outside of htw_reset(), we need to ensure that c0 changes have been propagated properly before we attempt to continue with subsequence memory operations. Signed-off-by: Markos Chandras Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/9114/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/include/asm/pgtable.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/arch/mips/include/asm/pgtable.h b/arch/mips/include/asm/pgtable.h index 62a6ba383d4fdf..45d7fd5e1466f7 100644 --- a/arch/mips/include/asm/pgtable.h +++ b/arch/mips/include/asm/pgtable.h @@ -99,16 +99,20 @@ extern void paging_init(void); #define htw_stop() \ do { \ - if (cpu_has_htw) \ + if (cpu_has_htw) { \ write_c0_pwctl(read_c0_pwctl() & \ ~(1 << MIPS_PWCTL_PWEN_SHIFT)); \ + back_to_back_c0_hazard(); \ + } \ } while(0) #define htw_start() \ do { \ - if (cpu_has_htw) \ + if (cpu_has_htw) { \ write_c0_pwctl(read_c0_pwctl() | \ (1 << MIPS_PWCTL_PWEN_SHIFT)); \ + back_to_back_c0_hazard(); \ + } \ } while(0) @@ -116,9 +120,7 @@ do { \ do { \ if (cpu_has_htw) { \ htw_stop(); \ - back_to_back_c0_hazard(); \ htw_start(); \ - back_to_back_c0_hazard(); \ } \ } while(0) From dea338963b12e603533bc6fbc6a50ee4bb70eb50 Mon Sep 17 00:00:00 2001 From: Markos Chandras Date: Mon, 26 Jan 2015 09:40:36 +0000 Subject: [PATCH 082/788] MIPS: asm: pgtable: Prevent HTW race when updating PTEs commit fde3538a8a711aedf1173ecb2d45aed868f51c97 upstream. Whenever we modify a page table entry, we need to ensure that the HTW will not fetch a stable entry. And for that to happen we need to ensure that HTW is stopped before we modify the said entry otherwise the HTW may already be in the process of reading that entry and fetching the old information. As a result of which, we replace the htw_reset() calls with htw_{stop,start} in more appropriate places. This also removes the remaining users of htw_reset() and as a result we drop that macro Signed-off-by: Markos Chandras Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/9116/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/include/asm/pgtable.h | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/arch/mips/include/asm/pgtable.h b/arch/mips/include/asm/pgtable.h index 45d7fd5e1466f7..3aa982b50a10de 100644 --- a/arch/mips/include/asm/pgtable.h +++ b/arch/mips/include/asm/pgtable.h @@ -116,14 +116,6 @@ do { \ } while(0) -#define htw_reset() \ -do { \ - if (cpu_has_htw) { \ - htw_stop(); \ - htw_start(); \ - } \ -} while(0) - extern void set_pte_at(struct mm_struct *mm, unsigned long addr, pte_t *ptep, pte_t pteval); @@ -155,12 +147,13 @@ static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *pt { pte_t null = __pte(0); + htw_stop(); /* Preserve global status for the pair */ if (ptep_buddy(ptep)->pte_low & _PAGE_GLOBAL) null.pte_low = null.pte_high = _PAGE_GLOBAL; set_pte_at(mm, addr, ptep, null); - htw_reset(); + htw_start(); } #else @@ -190,6 +183,7 @@ static inline void set_pte(pte_t *ptep, pte_t pteval) static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep) { + htw_stop(); #if !defined(CONFIG_CPU_R3000) && !defined(CONFIG_CPU_TX39XX) /* Preserve global status for the pair */ if (pte_val(*ptep_buddy(ptep)) & _PAGE_GLOBAL) @@ -197,7 +191,7 @@ static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *pt else #endif set_pte_at(mm, addr, ptep, __pte(0)); - htw_reset(); + htw_start(); } #endif From 2bfca500fb41ad36b78db33d7c66502fc1f48cf1 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Tue, 10 Feb 2015 10:02:59 +0000 Subject: [PATCH 083/788] MIPS: Export FP functions used by lose_fpu(1) for KVM commit 3ce465e04bfd8de9956d515d6e9587faac3375dc upstream. Export the _save_fp asm function used by the lose_fpu(1) macro to GPL modules so that KVM can make use of it when it is built as a module. This fixes the following build error when CONFIG_KVM=m due to commit f798217dfd03 ("KVM: MIPS: Don't leak FPU/DSP to guest"): ERROR: "_save_fp" [arch/mips/kvm/kvm.ko] undefined! Signed-off-by: James Hogan Fixes: f798217dfd03 (KVM: MIPS: Don't leak FPU/DSP to guest) Cc: Paolo Bonzini Cc: Ralf Baechle Cc: Paul Burton Cc: Gleb Natapov Cc: kvm@vger.kernel.org Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/9260/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/kernel/mips_ksyms.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/mips/kernel/mips_ksyms.c b/arch/mips/kernel/mips_ksyms.c index 17eaf0cf760c60..167ce3fae398a6 100644 --- a/arch/mips/kernel/mips_ksyms.c +++ b/arch/mips/kernel/mips_ksyms.c @@ -14,6 +14,7 @@ #include #include #include +#include extern void *__bzero(void *__s, size_t __count); extern long __strncpy_from_kernel_nocheck_asm(char *__to, @@ -31,6 +32,11 @@ extern long __strnlen_kernel_asm(const char *s); extern long __strnlen_user_nocheck_asm(const char *s); extern long __strnlen_user_asm(const char *s); +/* + * Core architecture code + */ +EXPORT_SYMBOL_GPL(_save_fp); + /* * String functions */ From a818d2aeecc08f755b61a2807ed4c83847563905 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Tue, 10 Feb 2015 10:03:00 +0000 Subject: [PATCH 084/788] MIPS: Export MSA functions used by lose_fpu(1) for KVM commit ca5d25642e212f73492d332d95dc90ef46a0e8dc upstream. Export the _save_msa asm function used by the lose_fpu(1) macro to GPL modules so that KVM can make use of it when it is built as a module. This fixes the following build error when CONFIG_KVM=m and CONFIG_CPU_HAS_MSA=y due to commit f798217dfd03 ("KVM: MIPS: Don't leak FPU/DSP to guest"): ERROR: "_save_msa" [arch/mips/kvm/kvm.ko] undefined! Fixes: f798217dfd03 (KVM: MIPS: Don't leak FPU/DSP to guest) Signed-off-by: James Hogan Cc: Paolo Bonzini Cc: Ralf Baechle Cc: Paul Burton Cc: Gleb Natapov Cc: kvm@vger.kernel.org Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/9261/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/kernel/mips_ksyms.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/mips/kernel/mips_ksyms.c b/arch/mips/kernel/mips_ksyms.c index 167ce3fae398a6..1a73c6c9e4b73b 100644 --- a/arch/mips/kernel/mips_ksyms.c +++ b/arch/mips/kernel/mips_ksyms.c @@ -15,6 +15,7 @@ #include #include #include +#include extern void *__bzero(void *__s, size_t __count); extern long __strncpy_from_kernel_nocheck_asm(char *__to, @@ -36,6 +37,9 @@ extern long __strnlen_user_asm(const char *s); * Core architecture code */ EXPORT_SYMBOL_GPL(_save_fp); +#ifdef CONFIG_CPU_HAS_MSA +EXPORT_SYMBOL_GPL(_save_msa); +#endif /* * String functions From 9cdaa7e29df63ad90f01b218b176425fd07de652 Mon Sep 17 00:00:00 2001 From: Naoya Horiguchi Date: Wed, 11 Feb 2015 15:25:19 -0800 Subject: [PATCH 085/788] mm/hugetlb: pmd_huge() returns true for non-present hugepage commit cbef8478bee55775ac312a574aad48af7bb9cf9f upstream. Migrating hugepages and hwpoisoned hugepages are considered as non-present hugepages, and they are referenced via migration entries and hwpoison entries in their page table slots. This behavior causes race condition because pmd_huge() doesn't tell non-huge pages from migrating/hwpoisoned hugepages. follow_page_mask() is one example where the kernel would call follow_page_pte() for such hugepage while this function is supposed to handle only normal pages. To avoid this, this patch makes pmd_huge() return true when pmd_none() is true *and* pmd_present() is false. We don't have to worry about mixing up non-present pmd entry with normal pmd (pointing to leaf level pte entry) because pmd_present() is true in normal pmd. The same race condition could happen in (x86-specific) gup_pmd_range(), where this patch simply adds pmd_present() check instead of pmd_huge(). This is because gup_pmd_range() is fast path. If we have non-present hugepage in this function, we will go into gup_huge_pmd(), then return 0 at flag mask check, and finally fall back to the slow path. Fixes: 290408d4a2 ("hugetlb: hugepage migration core") Signed-off-by: Naoya Horiguchi Cc: Hugh Dickins Cc: James Hogan Cc: David Rientjes Cc: Mel Gorman Cc: Johannes Weiner Cc: Michal Hocko Cc: Rik van Riel Cc: Andrea Arcangeli Cc: Luiz Capitulino Cc: Nishanth Aravamudan Cc: Lee Schermerhorn Cc: Steve Capper Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- arch/x86/mm/gup.c | 2 +- arch/x86/mm/hugetlbpage.c | 8 +++++++- mm/hugetlb.c | 2 ++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/arch/x86/mm/gup.c b/arch/x86/mm/gup.c index d7547824e76369..224b14235e967b 100644 --- a/arch/x86/mm/gup.c +++ b/arch/x86/mm/gup.c @@ -172,7 +172,7 @@ static int gup_pmd_range(pud_t pud, unsigned long addr, unsigned long end, */ if (pmd_none(pmd) || pmd_trans_splitting(pmd)) return 0; - if (unlikely(pmd_large(pmd))) { + if (unlikely(pmd_large(pmd) || !pmd_present(pmd))) { /* * NUMA hinting faults need to be handled in the GUP * slowpath for accounting purposes and so that they diff --git a/arch/x86/mm/hugetlbpage.c b/arch/x86/mm/hugetlbpage.c index 8b977ebf9388c4..006cc914994b73 100644 --- a/arch/x86/mm/hugetlbpage.c +++ b/arch/x86/mm/hugetlbpage.c @@ -66,9 +66,15 @@ follow_huge_addr(struct mm_struct *mm, unsigned long address, int write) return ERR_PTR(-EINVAL); } +/* + * pmd_huge() returns 1 if @pmd is hugetlb related entry, that is normal + * hugetlb entry or non-present (migration or hwpoisoned) hugetlb entry. + * Otherwise, returns 0. + */ int pmd_huge(pmd_t pmd) { - return !!(pmd_val(pmd) & _PAGE_PSE); + return !pmd_none(pmd) && + (pmd_val(pmd) & (_PAGE_PRESENT|_PAGE_PSE)) != _PAGE_PRESENT; } int pud_huge(pud_t pud) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 85032de5e20f88..c49586f407584e 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -3666,6 +3666,8 @@ follow_huge_pmd(struct mm_struct *mm, unsigned long address, { struct page *page; + if (!pmd_present(*pmd)) + return NULL; page = pte_page(*(pte_t *)pmd); if (page) page += ((address & ~PMD_MASK) >> PAGE_SHIFT); From a0a645bf699f808e997fc7caa7cdeb459251bcdf Mon Sep 17 00:00:00 2001 From: Vikram Mulukutla Date: Wed, 17 Dec 2014 18:50:56 -0800 Subject: [PATCH 086/788] tracing: Fix unmapping loop in tracing_mark_write commit 7215853e985a4bef1a6c14e00e89dfec84f1e457 upstream. Commit 6edb2a8a385f0cdef51dae37ff23e74d76d8a6ce introduced an array map_pages that contains the addresses returned by kmap_atomic. However, when unmapping those pages, map_pages[0] is unmapped before map_pages[1], breaking the nesting requirement as specified in the documentation for kmap_atomic/kunmap_atomic. This was caught by the highmem debug code present in kunmap_atomic. Fix the loop to do the unmapping properly. Link: http://lkml.kernel.org/r/1418871056-6614-1-git-send-email-markivx@codeaurora.org Reviewed-by: Stephen Boyd Reported-by: Lime Yang Signed-off-by: Vikram Mulukutla Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 4a9079b9f082fd..361a827b4962e8 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -4942,7 +4942,7 @@ tracing_mark_write(struct file *filp, const char __user *ubuf, *fpos += written; out_unlock: - for (i = 0; i < nr_pages; i++){ + for (i = nr_pages - 1; i >= 0; i--) { kunmap_atomic(map_page[i]); put_page(pages[i]); } From 238ddf7ac40a14b51d8317246263494406d2c6ba Mon Sep 17 00:00:00 2001 From: Tony Battersby Date: Wed, 11 Feb 2015 11:32:30 -0500 Subject: [PATCH 087/788] blk-mq: fix double-free in error path commit 564e559f2baf6a868768d0cac286980b3cfd6e30 upstream. If the allocation of bt->bs fails, then bt->map can be freed twice, once in blk_mq_init_bitmap_tags() -> bt_alloc(), and once in blk_mq_init_bitmap_tags() -> bt_free(). Fix by setting the pointer to NULL after the first free. Signed-off-by: Tony Battersby Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/blk-mq-tag.c | 1 + 1 file changed, 1 insertion(+) diff --git a/block/blk-mq-tag.c b/block/blk-mq-tag.c index 60c9d4a93fe470..3a415ecfe3d4fb 100644 --- a/block/blk-mq-tag.c +++ b/block/blk-mq-tag.c @@ -509,6 +509,7 @@ static int bt_alloc(struct blk_mq_bitmap_tags *bt, unsigned int depth, bt->bs = kzalloc(BT_WAIT_QUEUES * sizeof(*bt->bs), GFP_KERNEL); if (!bt->bs) { kfree(bt->map); + bt->map = NULL; return -ENOMEM; } From 33ceb68c181a9ab8d6d90e4a8204964ea6e7a765 Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Thu, 15 Jan 2015 03:06:22 +0100 Subject: [PATCH 088/788] ARM: 8284/1: sa1100: clear RCSR_SMR on resume commit e461894dc2ce7778ccde1c3483c9b15a85a7fc5f upstream. StrongARM core uses RCSR SMR bit to tell to bootloader that it was reset by entering the sleep mode. After we have resumed, there is little point in having that bit enabled. Moreover, if this bit is set before reboot, the bootloader can become confused. Thus clear the SMR bit on resume just before clearing the scratchpad (resume address) register. Signed-off-by: Dmitry Eremin-Solenikov Signed-off-by: Russell King Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-sa1100/pm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-sa1100/pm.c b/arch/arm/mach-sa1100/pm.c index 6645d1e31f14d8..34853d5dfda28b 100644 --- a/arch/arm/mach-sa1100/pm.c +++ b/arch/arm/mach-sa1100/pm.c @@ -81,6 +81,7 @@ static int sa11x0_pm_enter(suspend_state_t state) /* * Ensure not to come back here if it wasn't intended */ + RCSR = RCSR_SMR; PSPR = 0; /* From 7c06223143b7e42637c04225988847ae7be96e04 Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Thu, 8 Jan 2015 17:22:04 +0530 Subject: [PATCH 089/788] ARM: DRA7: hwmod: Fix boot crash with DEBUG_LL enabled on UART3 commit 1c7e36bfc3e2fb2df5e2d1989a4b6fb9055a0f9b upstream. With commit '7dedd34: ARM: OMAP2+: hwmod: Fix a crash in _setup_reset() with DEBUG_LL' we moved from parsing cmdline to identify uart used for earlycon to using the requsite hwmod CONFIG_DEBUG_OMAPxUARTy FLAGS. On DRA7 UART3 hwmod doesn't have this flag enabled, and atleast on BeagleBoard-X15, where we use UART3 for console, boot fails with DEBUG_LL enabled. Enable DEBUG_OMAP4UART3_FLAGS for UART3 hwmod. For using DEBUG_LL, enable CONFIG_DEBUG_OMAP4UART3 in menuconfig. Fixes: 90020c7b2c5e ("ARM: OMAP: DRA7: hwmod: Create initial DRA7XX SoC data") Reviewed-by: Felipe Balbi Acked-by: Felipe Balbi Signed-off-by: Lokesh Vutla Signed-off-by: Paul Walmsley Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-omap2/omap_hwmod_7xx_data.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-omap2/omap_hwmod_7xx_data.c b/arch/arm/mach-omap2/omap_hwmod_7xx_data.c index ffd6604cd54643..b6ea88f5399e56 100644 --- a/arch/arm/mach-omap2/omap_hwmod_7xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_7xx_data.c @@ -2017,7 +2017,7 @@ static struct omap_hwmod dra7xx_uart3_hwmod = { .class = &dra7xx_uart_hwmod_class, .clkdm_name = "l4per_clkdm", .main_clk = "uart3_gfclk_mux", - .flags = HWMOD_SWSUP_SIDLE_ACT, + .flags = HWMOD_SWSUP_SIDLE_ACT | DEBUG_OMAP4UART3_FLAGS, .prcm = { .omap4 = { .clkctrl_offs = DRA7XX_CM_L4PER_UART3_CLKCTRL_OFFSET, From 6fda79f84cf909ba09889b7f7087fee8e4e166d6 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Fri, 12 Dec 2014 18:19:19 +0300 Subject: [PATCH 090/788] ARM: dts: tegra20: fix GR3D, DSI unit and reg base addresses commit de47699d005996b41cea590c6098078ac12058be upstream. Commit 58ecb23f64ee ("ARM: tegra: add missing unit addresses to DT") added unit address and changed reg base for GR3D and DSI host1x modules, but these addresses belongs to GR2D and TVO modules respectively. Fix it by changing modules unit and reg base addresses to proper ones. Signed-off-by: Dmitry Osipenko Fixes: 58ecb23f64ee (ARM: tegra: add missing unit addresses to DT) Reviewed-by: Alexandre Courbot Signed-off-by: Thierry Reding Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/tegra20.dtsi | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi index 8acf5d85c99da5..f76fe94267d6c3 100644 --- a/arch/arm/boot/dts/tegra20.dtsi +++ b/arch/arm/boot/dts/tegra20.dtsi @@ -68,9 +68,9 @@ reset-names = "2d"; }; - gr3d@54140000 { + gr3d@54180000 { compatible = "nvidia,tegra20-gr3d"; - reg = <0x54140000 0x00040000>; + reg = <0x54180000 0x00040000>; clocks = <&tegra_car TEGRA20_CLK_GR3D>; resets = <&tegra_car 24>; reset-names = "3d"; @@ -130,9 +130,9 @@ status = "disabled"; }; - dsi@542c0000 { + dsi@54300000 { compatible = "nvidia,tegra20-dsi"; - reg = <0x542c0000 0x00040000>; + reg = <0x54300000 0x00040000>; clocks = <&tegra_car TEGRA20_CLK_DSI>; resets = <&tegra_car 48>; reset-names = "dsi"; From 42b3f837879d1d8539b2a9f57fd4d6417f5d2a66 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Tue, 24 Feb 2015 10:10:43 -0600 Subject: [PATCH 091/788] ARM: dts: am335x-bone*: usb0 is hardwired for peripheral commit 67fd14b3eca63b14429350e9eadc5fab709a8821 upstream. Fixes: http://bugs.elinux.org/issues/127 the bb.org community was seeing random reboots before this change. Signed-off-by: Robert Nelson Reviewed-by: Felipe Balbi Acked-by: Felipe Balbi Signed-off-by: Tony Lindgren Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/am335x-bone-common.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/am335x-bone-common.dtsi b/arch/arm/boot/dts/am335x-bone-common.dtsi index 6cc25ed912eeff..2c6248d9a9efcd 100644 --- a/arch/arm/boot/dts/am335x-bone-common.dtsi +++ b/arch/arm/boot/dts/am335x-bone-common.dtsi @@ -195,6 +195,7 @@ &usb0 { status = "okay"; + dr_mode = "peripheral"; }; &usb1 { From ede1c87dab875cb070f4f4d00a6e19315f6163c8 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 10 Feb 2015 17:33:07 -0800 Subject: [PATCH 092/788] ARM: dts: BCM63xx: fix L2 cache properties commit 9df11828d9b5665ddef81e45f83dd5376a8cd620 upstream. The L2 cache properties were completely off with respect to what the hardware is configured for. Fix the cache-size, cache-line-size and cache-sets to reflect the L2 cache controller we have: 512KB, 16 ways and 32 bytes per cache-line. Fixes: 46d4bca0445a0 ("ARM: BCM63XX: add BCM63138 minimal Device Tree") Signed-off-by: Florian Fainelli Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/bcm63138.dtsi | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/bcm63138.dtsi b/arch/arm/boot/dts/bcm63138.dtsi index d2d8e94e0aa204..f46329c8ad75c0 100644 --- a/arch/arm/boot/dts/bcm63138.dtsi +++ b/arch/arm/boot/dts/bcm63138.dtsi @@ -66,8 +66,9 @@ reg = <0x1d000 0x1000>; cache-unified; cache-level = <2>; - cache-sets = <16>; - cache-size = <0x80000>; + cache-size = <524288>; + cache-sets = <1024>; + cache-line-size = <32>; interrupts = ; }; From bef49dc168f4f3cbe5a94588c46e4ea260b1dd32 Mon Sep 17 00:00:00 2001 From: Scot Doyle Date: Wed, 24 Sep 2014 22:41:10 +0000 Subject: [PATCH 093/788] tpm_tis: verify interrupt during init commit 448e9c55c12d6bd4fa90a7e31d802e045666d7c8 upstream. Some machines, such as the Acer C720 and Toshiba CB35, have TPMs that do not send IRQs while also having an ACPI TPM entry indicating that they will be sent. These machines freeze on resume while the tpm_tis module waits for an IRQ, eventually timing out. When in interrupt mode, the tpm_tis module should receive an IRQ during module init. Fall back to polling mode if none is received when expected. Signed-off-by: Scot Doyle Tested-by: Michael Mullin Reviewed-by: Jason Gunthorpe [phuewe: minor checkpatch fixed] Signed-off-by: Peter Huewe Signed-off-by: Greg Kroah-Hartman --- drivers/char/tpm/tpm_tis.c | 76 +++++++++++++++++++++++++++++++------- 1 file changed, 62 insertions(+), 14 deletions(-) diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 6f198549611222..ccb140d605327d 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -75,6 +75,10 @@ enum tis_defaults { #define TPM_DID_VID(l) (0x0F00 | ((l) << 12)) #define TPM_RID(l) (0x0F04 | ((l) << 12)) +struct priv_data { + bool irq_tested; +}; + static LIST_HEAD(tis_chips); static DEFINE_MUTEX(tis_lock); @@ -338,12 +342,27 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len) return rc; } +static void disable_interrupts(struct tpm_chip *chip) +{ + u32 intmask; + + intmask = + ioread32(chip->vendor.iobase + + TPM_INT_ENABLE(chip->vendor.locality)); + intmask &= ~TPM_GLOBAL_INT_ENABLE; + iowrite32(intmask, + chip->vendor.iobase + + TPM_INT_ENABLE(chip->vendor.locality)); + free_irq(chip->vendor.irq, chip); + chip->vendor.irq = 0; +} + /* * If interrupts are used (signaled by an irq set in the vendor structure) * tpm.c can skip polling for the data to be available as the interrupt is * waited for here */ -static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) +static int tpm_tis_send_main(struct tpm_chip *chip, u8 *buf, size_t len) { int rc; u32 ordinal; @@ -373,6 +392,30 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) return rc; } +static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) +{ + int rc, irq; + struct priv_data *priv = chip->vendor.priv; + + if (!chip->vendor.irq || priv->irq_tested) + return tpm_tis_send_main(chip, buf, len); + + /* Verify receipt of the expected IRQ */ + irq = chip->vendor.irq; + chip->vendor.irq = 0; + rc = tpm_tis_send_main(chip, buf, len); + chip->vendor.irq = irq; + if (!priv->irq_tested) + msleep(1); + if (!priv->irq_tested) { + disable_interrupts(chip); + dev_err(chip->dev, + FW_BUG "TPM interrupt not working, polling instead\n"); + } + priv->irq_tested = true; + return rc; +} + struct tis_vendor_timeout_override { u32 did_vid; unsigned long timeout_us[4]; @@ -505,6 +548,7 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id) if (interrupt == 0) return IRQ_NONE; + ((struct priv_data *)chip->vendor.priv)->irq_tested = true; if (interrupt & TPM_INTF_DATA_AVAIL_INT) wake_up_interruptible(&chip->vendor.read_queue); if (interrupt & TPM_INTF_LOCALITY_CHANGE_INT) @@ -534,9 +578,14 @@ static int tpm_tis_init(struct device *dev, resource_size_t start, u32 vendor, intfcaps, intmask; int rc, i, irq_s, irq_e, probe; struct tpm_chip *chip; + struct priv_data *priv; + priv = devm_kzalloc(dev, sizeof(struct priv_data), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; if (!(chip = tpm_register_hardware(dev, &tpm_tis))) return -ENODEV; + chip->vendor.priv = priv; chip->vendor.iobase = ioremap(start, len); if (!chip->vendor.iobase) { @@ -605,19 +654,6 @@ static int tpm_tis_init(struct device *dev, resource_size_t start, if (intfcaps & TPM_INTF_DATA_AVAIL_INT) dev_dbg(dev, "\tData Avail Int Support\n"); - /* get the timeouts before testing for irqs */ - if (tpm_get_timeouts(chip)) { - dev_err(dev, "Could not get TPM timeouts and durations\n"); - rc = -ENODEV; - goto out_err; - } - - if (tpm_do_selftest(chip)) { - dev_err(dev, "TPM self test failed\n"); - rc = -ENODEV; - goto out_err; - } - /* INTERRUPT Setup */ init_waitqueue_head(&chip->vendor.read_queue); init_waitqueue_head(&chip->vendor.int_queue); @@ -719,6 +755,18 @@ static int tpm_tis_init(struct device *dev, resource_size_t start, } } + if (tpm_get_timeouts(chip)) { + dev_err(dev, "Could not get TPM timeouts and durations\n"); + rc = -ENODEV; + goto out_err; + } + + if (tpm_do_selftest(chip)) { + dev_err(dev, "TPM self test failed\n"); + rc = -ENODEV; + goto out_err; + } + INIT_LIST_HEAD(&chip->vendor.list); mutex_lock(&tis_lock); list_add(&chip->vendor.list, &tis_chips); From 770ab65c5cb33869bbfb5439b78925573246c51a Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 29 Aug 2014 10:33:02 +0100 Subject: [PATCH 094/788] TPM: Add new TPMs to the tail of the list to prevent inadvertent change of dev commit 398a1e71dc827b994b7f2f56c7c2186fea7f8d75 upstream. Add newly registered TPMs to the tail of the list, not the beginning, so that things that are specifying TPM_ANY_NUM don't find that the device they're using has inadvertently changed. Adding a second device would break IMA, for instance. Signed-off-by: David Howells Reviewed-by: Jason Gunthorpe Signed-off-by: Peter Huewe Signed-off-by: Greg Kroah-Hartman --- drivers/char/tpm/tpm-interface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 6af17002a11533..cfb9089887bd86 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -1122,7 +1122,7 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, /* Make chip available */ spin_lock(&driver_lock); - list_add_rcu(&chip->list, &tpm_chip_list); + list_add_tail_rcu(&chip->list, &tpm_chip_list); spin_unlock(&driver_lock); return chip; From d9b736e02bd932cde59a18c6cf7de7d64277be21 Mon Sep 17 00:00:00 2001 From: Kiran Padwal Date: Fri, 19 Sep 2014 12:44:39 +0530 Subject: [PATCH 095/788] char: tpm: Add missing error check for devm_kzalloc commit bb95cd34ba4c9467114acc78eeddd53ab1c10085 upstream. Currently these driver are missing a check on the return value of devm_kzalloc, which would cause a NULL pointer dereference in a OOM situation. This patch adds a missing check for tpm_i2c_atmel.c and tpm_i2c_nuvoton.c Signed-off-by: Kiran Padwal Reviewed-By: Jason Gunthorpe Signed-off-by: Peter Huewe Signed-off-by: Greg Kroah-Hartman --- drivers/char/tpm/tpm_i2c_atmel.c | 4 ++++ drivers/char/tpm/tpm_i2c_nuvoton.c | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/drivers/char/tpm/tpm_i2c_atmel.c b/drivers/char/tpm/tpm_i2c_atmel.c index 77272925dee6dd..503a85ae176cdc 100644 --- a/drivers/char/tpm/tpm_i2c_atmel.c +++ b/drivers/char/tpm/tpm_i2c_atmel.c @@ -168,6 +168,10 @@ static int i2c_atmel_probe(struct i2c_client *client, chip->vendor.priv = devm_kzalloc(dev, sizeof(struct priv_data), GFP_KERNEL); + if (!chip->vendor.priv) { + rc = -ENOMEM; + goto out_err; + } /* Default timeouts */ chip->vendor.timeout_a = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT); diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c index 7b158efd49f7ad..23c7b137a7fdb4 100644 --- a/drivers/char/tpm/tpm_i2c_nuvoton.c +++ b/drivers/char/tpm/tpm_i2c_nuvoton.c @@ -538,6 +538,11 @@ static int i2c_nuvoton_probe(struct i2c_client *client, chip->vendor.priv = devm_kzalloc(dev, sizeof(struct priv_data), GFP_KERNEL); + if (!chip->vendor.priv) { + rc = -ENOMEM; + goto out_err; + } + init_waitqueue_head(&chip->vendor.read_queue); init_waitqueue_head(&chip->vendor.int_queue); From f6dfa6a2c9fd04a2496e75368bacdfd027f197d8 Mon Sep 17 00:00:00 2001 From: "Hon Ching (Vicky) Lo" Date: Sun, 30 Nov 2014 15:01:28 +0100 Subject: [PATCH 096/788] tpm: Fix NULL return in tpm_ibmvtpm_get_desired_dma commit 84eb186bc37c0900b53077ca21cf6dd15823a232 upstream. There was an oops in tpm_ibmvtpm_get_desired_dma, which caused kernel panic during boot when vTPM is enabled in Power partition configured in AMS mode. vio_bus_probe calls vio_cmo_bus_probe which calls tpm_ibmvtpm_get_desired_dma to get the size needed for DMA allocation. The problem is, vio_cmo_bus_probe is called before calling probe, which for vtpm is tpm_ibmvtpm_probe and it's this function that initializes and sets up vtpm's CRQ and gets required data values. Therefore, since this has not yet been done, NULL is returned in attempt to get the size for DMA allocation. We added a NULL check. In addition, a default buffer size will be set when NULL is returned. Signed-off-by: Hon Ching (Vicky) Lo Signed-off-by: Peter Huewe Signed-off-by: Greg Kroah-Hartman --- drivers/char/tpm/tpm_ibmvtpm.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/char/tpm/tpm_ibmvtpm.c b/drivers/char/tpm/tpm_ibmvtpm.c index af74c57e509094..4109222f2878c1 100644 --- a/drivers/char/tpm/tpm_ibmvtpm.c +++ b/drivers/char/tpm/tpm_ibmvtpm.c @@ -307,6 +307,14 @@ static int tpm_ibmvtpm_remove(struct vio_dev *vdev) static unsigned long tpm_ibmvtpm_get_desired_dma(struct vio_dev *vdev) { struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(&vdev->dev); + + /* ibmvtpm initializes at probe time, so the data we are + * asking for may not be set yet. Estimate that 4K required + * for TCE-mapped buffer in addition to CRQ. + */ + if (!ibmvtpm) + return CRQ_RES_BUF_SIZE + PAGE_SIZE; + return CRQ_RES_BUF_SIZE + ibmvtpm->rtce_size; } From 0d786782b4974551bed70216087f8ff840eeb2e6 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Mon, 1 Dec 2014 19:32:46 +0100 Subject: [PATCH 097/788] tpm/tpm_i2c_stm_st33: Fix potential bug in tpm_stm_i2c_send commit 1ba3b0b6f218072afe8372d12f1b6bf26a26008e upstream. When sending data in tpm_stm_i2c_send, each loop iteration send buf. Send buf + i instead as the goal of this for loop is to send a number of byte from buf that fit in burstcnt. Once those byte are sent, we are supposed to send the next ones. The driver was working because the burstcount value returns always the maximum size for a TPM command or response. (0x800 for a command and 0x400 for a response). Reviewed-by: Jason Gunthorpe Signed-off-by: Christophe Ricard Signed-off-by: Peter Huewe Signed-off-by: Greg Kroah-Hartman --- drivers/char/tpm/tpm_i2c_stm_st33.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/char/tpm/tpm_i2c_stm_st33.c b/drivers/char/tpm/tpm_i2c_stm_st33.c index 4669e371342850..7d1c540fa26a89 100644 --- a/drivers/char/tpm/tpm_i2c_stm_st33.c +++ b/drivers/char/tpm/tpm_i2c_stm_st33.c @@ -487,7 +487,7 @@ static int tpm_stm_i2c_send(struct tpm_chip *chip, unsigned char *buf, if (burstcnt < 0) return burstcnt; size = min_t(int, len - i - 1, burstcnt); - ret = I2C_WRITE_DATA(client, TPM_DATA_FIFO, buf, size); + ret = I2C_WRITE_DATA(client, TPM_DATA_FIFO, buf + i, size); if (ret < 0) goto out_err; From e1f02461a776f3ee92564d5055b588eb8099e13f Mon Sep 17 00:00:00 2001 From: honclo Date: Thu, 12 Feb 2015 21:02:24 -0500 Subject: [PATCH 098/788] Added Little Endian support to vtpm module commit eb71f8a5e33fa1066fb92f0111ab366a341e1f6c upstream. The tpm_ibmvtpm module is affected by an unaligned access problem. ibmvtpm_crq_get_version failed with rc=-4 during boot when vTPM is enabled in Power partition, which supports both little endian and big endian modes. We added little endian support to fix this problem: 1) added cpu_to_be64 calls to ensure BE data is sent from an LE OS. 2) added be16_to_cpu and be32_to_cpu calls to make sure data received is in LE format on a LE OS. Signed-off-by: Hon Ching(Vicky) Lo Signed-off-by: Joy Latten [phuewe: manually applied the patch :( ] Reviewed-by: Ashley Lai Signed-off-by: Peter Huewe Signed-off-by: Greg Kroah-Hartman --- drivers/char/tpm/tpm_ibmvtpm.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/char/tpm/tpm_ibmvtpm.c b/drivers/char/tpm/tpm_ibmvtpm.c index 4109222f2878c1..eff9d587003484 100644 --- a/drivers/char/tpm/tpm_ibmvtpm.c +++ b/drivers/char/tpm/tpm_ibmvtpm.c @@ -148,7 +148,8 @@ static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) crq.len = (u16)count; crq.data = ibmvtpm->rtce_dma_handle; - rc = ibmvtpm_send_crq(ibmvtpm->vdev, word[0], word[1]); + rc = ibmvtpm_send_crq(ibmvtpm->vdev, cpu_to_be64(word[0]), + cpu_to_be64(word[1])); if (rc != H_SUCCESS) { dev_err(ibmvtpm->dev, "tpm_ibmvtpm_send failed rc=%d\n", rc); rc = 0; @@ -186,7 +187,8 @@ static int ibmvtpm_crq_get_rtce_size(struct ibmvtpm_dev *ibmvtpm) crq.valid = (u8)IBMVTPM_VALID_CMD; crq.msg = (u8)VTPM_GET_RTCE_BUFFER_SIZE; - rc = ibmvtpm_send_crq(ibmvtpm->vdev, buf[0], buf[1]); + rc = ibmvtpm_send_crq(ibmvtpm->vdev, cpu_to_be64(buf[0]), + cpu_to_be64(buf[1])); if (rc != H_SUCCESS) dev_err(ibmvtpm->dev, "ibmvtpm_crq_get_rtce_size failed rc=%d\n", rc); @@ -212,7 +214,8 @@ static int ibmvtpm_crq_get_version(struct ibmvtpm_dev *ibmvtpm) crq.valid = (u8)IBMVTPM_VALID_CMD; crq.msg = (u8)VTPM_GET_VERSION; - rc = ibmvtpm_send_crq(ibmvtpm->vdev, buf[0], buf[1]); + rc = ibmvtpm_send_crq(ibmvtpm->vdev, cpu_to_be64(buf[0]), + cpu_to_be64(buf[1])); if (rc != H_SUCCESS) dev_err(ibmvtpm->dev, "ibmvtpm_crq_get_version failed rc=%d\n", rc); @@ -335,7 +338,8 @@ static int tpm_ibmvtpm_suspend(struct device *dev) crq.valid = (u8)IBMVTPM_VALID_CMD; crq.msg = (u8)VTPM_PREPARE_TO_SUSPEND; - rc = ibmvtpm_send_crq(ibmvtpm->vdev, buf[0], buf[1]); + rc = ibmvtpm_send_crq(ibmvtpm->vdev, cpu_to_be64(buf[0]), + cpu_to_be64(buf[1])); if (rc != H_SUCCESS) dev_err(ibmvtpm->dev, "tpm_ibmvtpm_suspend failed rc=%d\n", rc); @@ -480,11 +484,11 @@ static void ibmvtpm_crq_process(struct ibmvtpm_crq *crq, case IBMVTPM_VALID_CMD: switch (crq->msg) { case VTPM_GET_RTCE_BUFFER_SIZE_RES: - if (crq->len <= 0) { + if (be16_to_cpu(crq->len) <= 0) { dev_err(ibmvtpm->dev, "Invalid rtce size\n"); return; } - ibmvtpm->rtce_size = crq->len; + ibmvtpm->rtce_size = be16_to_cpu(crq->len); ibmvtpm->rtce_buf = kmalloc(ibmvtpm->rtce_size, GFP_KERNEL); if (!ibmvtpm->rtce_buf) { @@ -505,11 +509,11 @@ static void ibmvtpm_crq_process(struct ibmvtpm_crq *crq, return; case VTPM_GET_VERSION_RES: - ibmvtpm->vtpm_version = crq->data; + ibmvtpm->vtpm_version = be32_to_cpu(crq->data); return; case VTPM_TPM_COMMAND_RES: /* len of the data in rtce buffer */ - ibmvtpm->res_len = crq->len; + ibmvtpm->res_len = be16_to_cpu(crq->len); wake_up_interruptible(&ibmvtpm->wq); return; default: From d52ff195d3faacb0228608aa8b2b1931d5bed21e Mon Sep 17 00:00:00 2001 From: Peng Tao Date: Sat, 24 Jan 2015 22:14:52 +0800 Subject: [PATCH 099/788] nfs41: .init_read and .init_write can be called with valid pg_lseg commit cb5d04bc39e914124e811ea55f3034d2379a5f6c upstream. With pgio refactoring in v3.15, .init_read and .init_write can be called with valid pgio->pg_lseg. file layout was fixed at that time by commit c6194271f (pnfs: filelayout: support non page aligned layouts). But the generic helper still needs to be fixed. Signed-off-by: Peng Tao Signed-off-by: Greg Kroah-Hartman --- fs/nfs/pnfs.c | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index 0a5dda4d85c27b..883ee88e5f5e6b 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -1445,19 +1445,19 @@ pnfs_generic_pg_init_read(struct nfs_pageio_descriptor *pgio, struct nfs_page *r { u64 rd_size = req->wb_bytes; - WARN_ON_ONCE(pgio->pg_lseg != NULL); - - if (pgio->pg_dreq == NULL) - rd_size = i_size_read(pgio->pg_inode) - req_offset(req); - else - rd_size = nfs_dreq_bytes_left(pgio->pg_dreq); - - pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode, - req->wb_context, - req_offset(req), - rd_size, - IOMODE_READ, - GFP_KERNEL); + if (pgio->pg_lseg == NULL) { + if (pgio->pg_dreq == NULL) + rd_size = i_size_read(pgio->pg_inode) - req_offset(req); + else + rd_size = nfs_dreq_bytes_left(pgio->pg_dreq); + + pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode, + req->wb_context, + req_offset(req), + rd_size, + IOMODE_READ, + GFP_KERNEL); + } /* If no lseg, fall back to read through mds */ if (pgio->pg_lseg == NULL) nfs_pageio_reset_read_mds(pgio); @@ -1469,14 +1469,13 @@ void pnfs_generic_pg_init_write(struct nfs_pageio_descriptor *pgio, struct nfs_page *req, u64 wb_size) { - WARN_ON_ONCE(pgio->pg_lseg != NULL); - - pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode, - req->wb_context, - req_offset(req), - wb_size, - IOMODE_RW, - GFP_NOFS); + if (pgio->pg_lseg == NULL) + pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode, + req->wb_context, + req_offset(req), + wb_size, + IOMODE_RW, + GFP_NOFS); /* If no lseg, fall back to write through mds */ if (pgio->pg_lseg == NULL) nfs_pageio_reset_write_mds(pgio); From 1a3f8e6fb70de61571ed10cdd023a29e2658f1db Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 30 Jan 2015 18:12:28 -0500 Subject: [PATCH 100/788] SUNRPC: NULL utsname dereference on NFS umount during namespace cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 03a9a42a1a7e5b3e7919ddfacc1d1cc81882a955 upstream. Fix an Oopsable condition when nsm_mon_unmon is called as part of the namespace cleanup, which now apparently happens after the utsname has been freed. Link: http://lkml.kernel.org/r/20150125220604.090121ae@neptune.home Reported-by: Bruno Prémont Signed-off-by: Trond Myklebust Signed-off-by: Greg Kroah-Hartman --- fs/lockd/mon.c | 13 +++++++++---- include/linux/sunrpc/clnt.h | 3 ++- net/sunrpc/clnt.c | 12 +++++++----- net/sunrpc/rpcb_clnt.c | 8 ++++++-- 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index 1cc6ec51e6b1f7..47a32b6d9b9001 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -65,7 +65,7 @@ static inline struct sockaddr *nsm_addr(const struct nsm_handle *nsm) return (struct sockaddr *)&nsm->sm_addr; } -static struct rpc_clnt *nsm_create(struct net *net) +static struct rpc_clnt *nsm_create(struct net *net, const char *nodename) { struct sockaddr_in sin = { .sin_family = AF_INET, @@ -77,6 +77,7 @@ static struct rpc_clnt *nsm_create(struct net *net) .address = (struct sockaddr *)&sin, .addrsize = sizeof(sin), .servername = "rpc.statd", + .nodename = nodename, .program = &nsm_program, .version = NSM_VERSION, .authflavor = RPC_AUTH_NULL, @@ -102,7 +103,7 @@ static struct rpc_clnt *nsm_client_set(struct lockd_net *ln, return clnt; } -static struct rpc_clnt *nsm_client_get(struct net *net) +static struct rpc_clnt *nsm_client_get(struct net *net, const char *nodename) { struct rpc_clnt *clnt, *new; struct lockd_net *ln = net_generic(net, lockd_net_id); @@ -111,7 +112,7 @@ static struct rpc_clnt *nsm_client_get(struct net *net) if (clnt != NULL) goto out; - clnt = new = nsm_create(net); + clnt = new = nsm_create(net, nodename); if (IS_ERR(clnt)) goto out; @@ -190,19 +191,23 @@ int nsm_monitor(const struct nlm_host *host) struct nsm_res res; int status; struct rpc_clnt *clnt; + const char *nodename = NULL; dprintk("lockd: nsm_monitor(%s)\n", nsm->sm_name); if (nsm->sm_monitored) return 0; + if (host->h_rpcclnt) + nodename = host->h_rpcclnt->cl_nodename; + /* * Choose whether to record the caller_name or IP address of * this peer in the local rpc.statd's database. */ nsm->sm_mon_name = nsm_use_hostnames ? nsm->sm_name : nsm->sm_addrbuf; - clnt = nsm_client_get(host->net); + clnt = nsm_client_get(host->net, nodename); if (IS_ERR(clnt)) { status = PTR_ERR(clnt); dprintk("lockd: failed to create NSM upcall transport, " diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index d86acc63b25fa1..598ba80ec30c97 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -57,7 +57,7 @@ struct rpc_clnt { const struct rpc_timeout *cl_timeout; /* Timeout strategy */ int cl_nodelen; /* nodename length */ - char cl_nodename[UNX_MAXNODENAME]; + char cl_nodename[UNX_MAXNODENAME+1]; struct rpc_pipe_dir_head cl_pipedir_objects; struct rpc_clnt * cl_parent; /* Points to parent of clones */ struct rpc_rtt cl_rtt_default; @@ -112,6 +112,7 @@ struct rpc_create_args { struct sockaddr *saddress; const struct rpc_timeout *timeout; const char *servername; + const char *nodename; const struct rpc_program *program; u32 prognumber; /* overrides program->number */ u32 version; diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 05da12a33945aa..3f5d4d48f0cbd2 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -286,10 +286,8 @@ static struct rpc_xprt *rpc_clnt_set_transport(struct rpc_clnt *clnt, static void rpc_clnt_set_nodename(struct rpc_clnt *clnt, const char *nodename) { - clnt->cl_nodelen = strlen(nodename); - if (clnt->cl_nodelen > UNX_MAXNODENAME) - clnt->cl_nodelen = UNX_MAXNODENAME; - memcpy(clnt->cl_nodename, nodename, clnt->cl_nodelen); + clnt->cl_nodelen = strlcpy(clnt->cl_nodename, + nodename, sizeof(clnt->cl_nodename)); } static int rpc_client_register(struct rpc_clnt *clnt, @@ -365,6 +363,7 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, const struct rpc_version *version; struct rpc_clnt *clnt = NULL; const struct rpc_timeout *timeout; + const char *nodename = args->nodename; int err; /* sanity check the name before trying to print it */ @@ -420,8 +419,10 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, atomic_set(&clnt->cl_count, 1); + if (nodename == NULL) + nodename = utsname()->nodename; /* save the nodename */ - rpc_clnt_set_nodename(clnt, utsname()->nodename); + rpc_clnt_set_nodename(clnt, nodename); err = rpc_client_register(clnt, args->authflavor, args->client_name); if (err) @@ -576,6 +577,7 @@ static struct rpc_clnt *__rpc_clone_client(struct rpc_create_args *args, if (xprt == NULL) goto out_err; args->servername = xprt->servername; + args->nodename = clnt->cl_nodename; new = rpc_new_client(args, xprt, clnt); if (IS_ERR(new)) { diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c index 05202012bcfca1..cf5770d8f49af9 100644 --- a/net/sunrpc/rpcb_clnt.c +++ b/net/sunrpc/rpcb_clnt.c @@ -355,7 +355,8 @@ int rpcb_create_local(struct net *net) return result; } -static struct rpc_clnt *rpcb_create(struct net *net, const char *hostname, +static struct rpc_clnt *rpcb_create(struct net *net, const char *nodename, + const char *hostname, struct sockaddr *srvaddr, size_t salen, int proto, u32 version) { @@ -365,6 +366,7 @@ static struct rpc_clnt *rpcb_create(struct net *net, const char *hostname, .address = srvaddr, .addrsize = salen, .servername = hostname, + .nodename = nodename, .program = &rpcb_program, .version = version, .authflavor = RPC_AUTH_UNIX, @@ -740,7 +742,9 @@ void rpcb_getport_async(struct rpc_task *task) dprintk("RPC: %5u %s: trying rpcbind version %u\n", task->tk_pid, __func__, bind_version); - rpcb_clnt = rpcb_create(xprt->xprt_net, xprt->servername, sap, salen, + rpcb_clnt = rpcb_create(xprt->xprt_net, + clnt->cl_nodename, + xprt->servername, sap, salen, xprt->prot, bind_version); if (IS_ERR(rpcb_clnt)) { status = PTR_ERR(rpcb_clnt); From d77b2f385a2f3850d1abab60ea145c38f795cb99 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 5 Feb 2015 15:13:24 -0500 Subject: [PATCH 101/788] NFSv4: Ensure we reference the inode for return-on-close in delegreturn commit ea7c38fef0b774a5dc16fb0ca5935f0ae8568176 upstream. If we have to do a return-on-close in the delegreturn code, then we must ensure that the inode and super block remain referenced. Cc: Peng Tao Signed-off-by: Trond Myklebust Reviewed-by: Peng Tao Signed-off-by: Greg Kroah-Hartman --- fs/nfs/internal.h | 22 +++++++++++++++++++++- fs/nfs/nfs4proc.c | 14 +++++++++----- fs/nfs/super.c | 9 ++++++--- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index b6f34bfa6fe83b..b84efe4b624708 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -375,7 +375,7 @@ extern struct rpc_stat nfs_rpcstat; extern int __init register_nfs_fs(void); extern void __exit unregister_nfs_fs(void); -extern void nfs_sb_active(struct super_block *sb); +extern bool nfs_sb_active(struct super_block *sb); extern void nfs_sb_deactive(struct super_block *sb); /* namespace.c */ @@ -493,6 +493,26 @@ extern int nfs41_walk_client_list(struct nfs_client *clp, struct nfs_client **result, struct rpc_cred *cred); +static inline struct inode *nfs_igrab_and_active(struct inode *inode) +{ + inode = igrab(inode); + if (inode != NULL && !nfs_sb_active(inode->i_sb)) { + iput(inode); + inode = NULL; + } + return inode; +} + +static inline void nfs_iput_and_deactive(struct inode *inode) +{ + if (inode != NULL) { + struct super_block *sb = inode->i_sb; + + iput(inode); + nfs_sb_deactive(sb); + } +} + /* * Determine the device name as a string */ diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index c347705b016104..89f6827302de88 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -5137,9 +5137,13 @@ static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata) static void nfs4_delegreturn_release(void *calldata) { struct nfs4_delegreturndata *data = calldata; + struct inode *inode = data->inode; - if (data->roc) - pnfs_roc_release(data->inode); + if (inode) { + if (data->roc) + pnfs_roc_release(inode); + nfs_iput_and_deactive(inode); + } kfree(calldata); } @@ -5196,9 +5200,9 @@ static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, co nfs_fattr_init(data->res.fattr); data->timestamp = jiffies; data->rpc_status = 0; - data->inode = inode; - data->roc = list_empty(&NFS_I(inode)->open_files) ? - pnfs_roc(inode) : false; + data->inode = nfs_igrab_and_active(inode); + if (data->inode) + data->roc = nfs4_roc(inode); task_setup_data.callback_data = data; msg.rpc_argp = &data->args; diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 31a11b0e885deb..368d9395d2e7f0 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -405,12 +405,15 @@ void __exit unregister_nfs_fs(void) unregister_filesystem(&nfs_fs_type); } -void nfs_sb_active(struct super_block *sb) +bool nfs_sb_active(struct super_block *sb) { struct nfs_server *server = NFS_SB(sb); - if (atomic_inc_return(&server->active) == 1) - atomic_inc(&sb->s_active); + if (!atomic_inc_not_zero(&sb->s_active)) + return false; + if (atomic_inc_return(&server->active) != 1) + atomic_dec(&sb->s_active); + return true; } EXPORT_SYMBOL_GPL(nfs_sb_active); From 1b4853b6d4cd02f97749bf0d6bfabc160b6f5077 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 11 Feb 2015 17:27:55 -0500 Subject: [PATCH 102/788] NFSv4.1: Fix a kfree() of uninitialised pointers in decode_cb_sequence_args commit d8ba1f971497c19cf80da1ea5391a46a5f9fbd41 upstream. If the call to decode_rc_list() fails due to a memory allocation error, then we need to truncate the array size to ensure that we only call kfree() on those pointer that were allocated. Reported-by: David Ramos Fixes: 4aece6a19cf7f ("nfs41: cb_sequence xdr implementation") Signed-off-by: Trond Myklebust Signed-off-by: Greg Kroah-Hartman --- fs/nfs/callback_xdr.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index f4ccfe6521ec80..02f8d09e119f4a 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c @@ -464,8 +464,10 @@ static __be32 decode_cb_sequence_args(struct svc_rqst *rqstp, for (i = 0; i < args->csa_nrclists; i++) { status = decode_rc_list(xdr, &args->csa_rclists[i]); - if (status) + if (status) { + args->csa_nrclists = i; goto out_free; + } } } status = 0; From 09c2814bd63d096180729ade91238b12f975e32f Mon Sep 17 00:00:00 2001 From: Tony Battersby Date: Fri, 13 Feb 2015 12:09:44 -0500 Subject: [PATCH 103/788] sg: fix unkillable I/O wait deadlock with scsi-mq commit 7568615c1054907ea8c7701ab86dad51aa099888 upstream. When using the write()/read() interface for submitting commands, the SCSI generic driver does not call blk_put_request() on a completed SCSI command until userspace calls read() to get the command completion. Since scsi-mq uses a fixed number of preallocated requests, this makes it possible for userspace to exhaust the entire preallocated supply of requests. For places in the kernel that call blk_get_request() with GFP_KERNEL, this can cause the calling process to deadlock in a permanent unkillable I/O wait in blk_get_request() -> ... -> bt_get(). For places in the kernel that call blk_get_request() with GFP_ATOMIC, this can cause blk_get_request() always to return -EWOULDBLOCK. Note that these problems happen only if scsi-mq is enabled. Prevent the problems by calling blk_put_request() as soon as the SCSI command completes instead of waiting for userspace to call read(). Signed-off-by: Tony Battersby Acked-by: Douglas Gilbert Tested-by: Douglas Gilbert Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/sg.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index b14f64cb97245c..57178eda815d9a 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -1350,6 +1350,17 @@ sg_rq_end_io(struct request *rq, int uptodate) } /* Rely on write phase to clean out srp status values, so no "else" */ + /* + * Free the request as soon as it is complete so that its resources + * can be reused without waiting for userspace to read() the + * result. But keep the associated bio (if any) around until + * blk_rq_unmap_user() can be called from user context. + */ + srp->rq = NULL; + if (rq->cmd != rq->__cmd) + kfree(rq->cmd); + __blk_put_request(rq->q, rq); + write_lock_irqsave(&sfp->rq_list_lock, iflags); if (unlikely(srp->orphan)) { if (sfp->keep_orphan) @@ -1777,10 +1788,10 @@ sg_finish_rem_req(Sg_request *srp) SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp, "sg_finish_rem_req: res_used=%d\n", (int) srp->res_used)); - if (srp->rq) { - if (srp->bio) - ret = blk_rq_unmap_user(srp->bio); + if (srp->bio) + ret = blk_rq_unmap_user(srp->bio); + if (srp->rq) { if (srp->rq->cmd != srp->rq->__cmd) kfree(srp->rq->cmd); blk_put_request(srp->rq); From e1ed769da5d4910885850a104d910d0495738d71 Mon Sep 17 00:00:00 2001 From: Tony Battersby Date: Fri, 13 Feb 2015 12:10:58 -0500 Subject: [PATCH 104/788] sg: fix EWOULDBLOCK errors with scsi-mq commit 7772855a996ec6e16944b120ab5ce21050279821 upstream. With scsi-mq enabled, userspace programs can get unexpected EWOULDBLOCK (a.k.a. EAGAIN) errors when submitting commands to the SCSI generic driver. Fix by calling blk_get_request() with GFP_KERNEL instead of GFP_ATOMIC. Note: to avoid introducing a potential deadlock, this patch should be applied after the patch titled "sg: fix unkillable I/O wait deadlock with scsi-mq". Signed-off-by: Tony Battersby Acked-by: Douglas Gilbert Tested-by: Douglas Gilbert Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/sg.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 57178eda815d9a..763bffe2351745 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -1695,7 +1695,22 @@ sg_start_req(Sg_request *srp, unsigned char *cmd) return -ENOMEM; } - rq = blk_get_request(q, rw, GFP_ATOMIC); + /* + * NOTE + * + * With scsi-mq enabled, there are a fixed number of preallocated + * requests equal in number to shost->can_queue. If all of the + * preallocated requests are already in use, then using GFP_ATOMIC with + * blk_get_request() will return -EWOULDBLOCK, whereas using GFP_KERNEL + * will cause blk_get_request() to sleep until an active command + * completes, freeing up a request. Neither option is ideal, but + * GFP_KERNEL is the better choice to prevent userspace from getting an + * unexpected EWOULDBLOCK. + * + * With scsi-mq disabled, blk_get_request() with GFP_KERNEL usually + * does not sleep except under memory pressure. + */ + rq = blk_get_request(q, rw, GFP_KERNEL); if (IS_ERR(rq)) { kfree(long_cmdp); return PTR_ERR(rq); From f623bcdd5b913d9a766a8282f22d31f4415f23fe Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Thu, 22 Jan 2015 00:56:53 -0800 Subject: [PATCH 105/788] iscsi-target: Drop problematic active_ts_list usage commit 3fd7b60f2c7418239d586e359e0c6d8503e10646 upstream. This patch drops legacy active_ts_list usage within iscsi_target_tq.c code. It was originally used to track the active thread sets during iscsi-target shutdown, and is no longer used by modern upstream code. Two people have reported list corruption using traditional iscsi-target and iser-target with the following backtrace, that appears to be related to iscsi_thread_set->ts_list being used across both active_ts_list and inactive_ts_list. [ 60.782534] ------------[ cut here ]------------ [ 60.782543] WARNING: CPU: 0 PID: 9430 at lib/list_debug.c:53 __list_del_entry+0x63/0xd0() [ 60.782545] list_del corruption, ffff88045b00d180->next is LIST_POISON1 (dead000000100100) [ 60.782546] Modules linked in: ib_srpt tcm_qla2xxx qla2xxx tcm_loop tcm_fc libfc scsi_transport_fc scsi_tgt ib_isert rdma_cm iw_cm ib_addr iscsi_target_mod target_core_pscsi target_core_file target_core_iblock target_core_mod configfs ebtable_nat ebtables ipt_MASQUERADE iptable_nat nf_nat_ipv4 nf_nat nf_conntrack_ipv4 nf_defrag_ipv4 ipt_REJECT xt_CHECKSUM iptable_mangle iptable_filter ip_tables bridge stp llc autofs4 sunrpc ip6t_REJECT nf_conntrack_ipv6 nf_defrag_ipv6 xt_state nf_conntrack ip6table_filter ip6_tables ipv6 ib_ipoib ib_cm ib_uverbs ib_umad mlx4_en mlx4_ib ib_sa ib_mad ib_core mlx4_core dm_mirror dm_region_hash dm_log dm_mod vhost_net macvtap macvlan vhost tun kvm_intel kvm uinput iTCO_wdt iTCO_vendor_support microcode serio_raw pcspkr sb_edac edac_core sg i2c_i801 lpc_ich mfd_core mtip32xx igb i2c_algo_bit i2c_core ptp pps_core ioatdma dca wmi ext3(F) jbd(F) mbcache(F) sd_mod(F) crc_t10dif(F) crct10dif_common(F) ahci(F) libahci(F) isci(F) libsas(F) scsi_transport_sas(F) [last unloaded: speedstep_lib] [ 60.782597] CPU: 0 PID: 9430 Comm: iscsi_ttx Tainted: GF 3.12.19+ #2 [ 60.782598] Hardware name: Supermicro X9DRX+-F/X9DRX+-F, BIOS 3.00 07/09/2013 [ 60.782599] 0000000000000035 ffff88044de31d08 ffffffff81553ae7 0000000000000035 [ 60.782602] ffff88044de31d58 ffff88044de31d48 ffffffff8104d1cc 0000000000000002 [ 60.782605] ffff88045b00d180 ffff88045b00d0c0 ffff88045b00d0c0 ffff88044de31e58 [ 60.782607] Call Trace: [ 60.782611] [] dump_stack+0x49/0x62 [ 60.782615] [] warn_slowpath_common+0x8c/0xc0 [ 60.782618] [] warn_slowpath_fmt+0x46/0x50 [ 60.782620] [] __list_del_entry+0x63/0xd0 [ 60.782622] [] list_del+0x11/0x40 [ 60.782630] [] iscsi_del_ts_from_active_list+0x29/0x50 [iscsi_target_mod] [ 60.782635] [] iscsi_tx_thread_pre_handler+0xa1/0x180 [iscsi_target_mod] [ 60.782642] [] iscsi_target_tx_thread+0x4e/0x220 [iscsi_target_mod] [ 60.782647] [] ? iscsit_handle_snack+0x190/0x190 [iscsi_target_mod] [ 60.782652] [] ? iscsit_handle_snack+0x190/0x190 [iscsi_target_mod] [ 60.782655] [] kthread+0xce/0xe0 [ 60.782657] [] ? kthread_freezable_should_stop+0x70/0x70 [ 60.782660] [] ret_from_fork+0x7c/0xb0 [ 60.782662] [] ? kthread_freezable_should_stop+0x70/0x70 [ 60.782663] ---[ end trace 9662f4a661d33965 ]--- Since this code is no longer used, go ahead and drop the problematic usage all-together. Reported-by: Gavin Guo Reported-by: Moussa Ba Signed-off-by: Nicholas Bellinger Signed-off-by: Greg Kroah-Hartman --- drivers/target/iscsi/iscsi_target_tq.c | 28 +++++--------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target_tq.c b/drivers/target/iscsi/iscsi_target_tq.c index 601e9cc61e98e7..bb2890e79ca001 100644 --- a/drivers/target/iscsi/iscsi_target_tq.c +++ b/drivers/target/iscsi/iscsi_target_tq.c @@ -24,36 +24,22 @@ #include "iscsi_target_tq.h" #include "iscsi_target.h" -static LIST_HEAD(active_ts_list); static LIST_HEAD(inactive_ts_list); -static DEFINE_SPINLOCK(active_ts_lock); static DEFINE_SPINLOCK(inactive_ts_lock); static DEFINE_SPINLOCK(ts_bitmap_lock); -static void iscsi_add_ts_to_active_list(struct iscsi_thread_set *ts) -{ - spin_lock(&active_ts_lock); - list_add_tail(&ts->ts_list, &active_ts_list); - iscsit_global->active_ts++; - spin_unlock(&active_ts_lock); -} - static void iscsi_add_ts_to_inactive_list(struct iscsi_thread_set *ts) { + if (!list_empty(&ts->ts_list)) { + WARN_ON(1); + return; + } spin_lock(&inactive_ts_lock); list_add_tail(&ts->ts_list, &inactive_ts_list); iscsit_global->inactive_ts++; spin_unlock(&inactive_ts_lock); } -static void iscsi_del_ts_from_active_list(struct iscsi_thread_set *ts) -{ - spin_lock(&active_ts_lock); - list_del(&ts->ts_list); - iscsit_global->active_ts--; - spin_unlock(&active_ts_lock); -} - static struct iscsi_thread_set *iscsi_get_ts_from_inactive_list(void) { struct iscsi_thread_set *ts; @@ -66,7 +52,7 @@ static struct iscsi_thread_set *iscsi_get_ts_from_inactive_list(void) ts = list_first_entry(&inactive_ts_list, struct iscsi_thread_set, ts_list); - list_del(&ts->ts_list); + list_del_init(&ts->ts_list); iscsit_global->inactive_ts--; spin_unlock(&inactive_ts_lock); @@ -204,8 +190,6 @@ static void iscsi_deallocate_extra_thread_sets(void) void iscsi_activate_thread_set(struct iscsi_conn *conn, struct iscsi_thread_set *ts) { - iscsi_add_ts_to_active_list(ts); - spin_lock_bh(&ts->ts_state_lock); conn->thread_set = ts; ts->conn = conn; @@ -397,7 +381,6 @@ struct iscsi_conn *iscsi_rx_thread_pre_handler(struct iscsi_thread_set *ts) if (ts->delay_inactive && (--ts->thread_count == 0)) { spin_unlock_bh(&ts->ts_state_lock); - iscsi_del_ts_from_active_list(ts); if (!iscsit_global->in_shutdown) iscsi_deallocate_extra_thread_sets(); @@ -452,7 +435,6 @@ struct iscsi_conn *iscsi_tx_thread_pre_handler(struct iscsi_thread_set *ts) if (ts->delay_inactive && (--ts->thread_count == 0)) { spin_unlock_bh(&ts->ts_state_lock); - iscsi_del_ts_from_active_list(ts); if (!iscsit_global->in_shutdown) iscsi_deallocate_extra_thread_sets(); From 3608406b794bc5bd25aae9bb5b08a53bff5629aa Mon Sep 17 00:00:00 2001 From: Konstantin Khlebnikov Date: Mon, 9 Feb 2015 16:42:49 +0300 Subject: [PATCH 106/788] cfq-iosched: handle failure of cfq group allocation commit 69abaffec7d47a083739b79e3066cb3730eba72e upstream. Cfq_lookup_create_cfqg() allocates struct blkcg_gq using GFP_ATOMIC. In cfq_find_alloc_queue() possible allocation failure is not handled. As a result kernel oopses on NULL pointer dereference when cfq_link_cfqq_cfqg() calls cfqg_get() for NULL pointer. Bug was introduced in v3.5 in commit cd1604fab4f9 ("blkcg: factor out blkio_group creation"). Prior to that commit cfq group lookup had returned pointer to root group as fallback. This patch handles this error using existing fallback oom_cfqq. Signed-off-by: Konstantin Khlebnikov Acked-by: Tejun Heo Acked-by: Vivek Goyal Fixes: cd1604fab4f9 ("blkcg: factor out blkio_group creation") Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/cfq-iosched.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 6f2751d305dede..01898a44709efd 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -3590,6 +3590,11 @@ cfq_find_alloc_queue(struct cfq_data *cfqd, bool is_sync, struct cfq_io_cq *cic, blkcg = bio_blkcg(bio); cfqg = cfq_lookup_create_cfqg(cfqd, blkcg); + if (!cfqg) { + cfqq = &cfqd->oom_cfqq; + goto out; + } + cfqq = cic_to_cfqq(cic, is_sync); /* @@ -3626,7 +3631,7 @@ cfq_find_alloc_queue(struct cfq_data *cfqd, bool is_sync, struct cfq_io_cq *cic, } else cfqq = &cfqd->oom_cfqq; } - +out: if (new_cfqq) kmem_cache_free(cfq_pool, new_cfqq); From 416f74c66eef66acebfbca90b647c5d123814325 Mon Sep 17 00:00:00 2001 From: Jeff Moyer Date: Mon, 12 Jan 2015 15:21:01 -0500 Subject: [PATCH 107/788] cfq-iosched: fix incorrect filing of rt async cfqq commit c6ce194325cef342313e3d27620411ce90a89c50 upstream. Hi, If you can manage to submit an async write as the first async I/O from the context of a process with realtime scheduling priority, then a cfq_queue is allocated, but filed into the wrong async_cfqq bucket. It ends up in the best effort array, but actually has realtime I/O scheduling priority set in cfqq->ioprio. The reason is that cfq_get_queue assumes the default scheduling class and priority when there is no information present (i.e. when the async cfqq is created): static struct cfq_queue * cfq_get_queue(struct cfq_data *cfqd, bool is_sync, struct cfq_io_cq *cic, struct bio *bio, gfp_t gfp_mask) { const int ioprio_class = IOPRIO_PRIO_CLASS(cic->ioprio); const int ioprio = IOPRIO_PRIO_DATA(cic->ioprio); cic->ioprio starts out as 0, which is "invalid". So, class of 0 (IOPRIO_CLASS_NONE) is passed to cfq_async_queue_prio like so: async_cfqq = cfq_async_queue_prio(cfqd, ioprio_class, ioprio); static struct cfq_queue ** cfq_async_queue_prio(struct cfq_data *cfqd, int ioprio_class, int ioprio) { switch (ioprio_class) { case IOPRIO_CLASS_RT: return &cfqd->async_cfqq[0][ioprio]; case IOPRIO_CLASS_NONE: ioprio = IOPRIO_NORM; /* fall through */ case IOPRIO_CLASS_BE: return &cfqd->async_cfqq[1][ioprio]; case IOPRIO_CLASS_IDLE: return &cfqd->async_idle_cfqq; default: BUG(); } } Here, instead of returning a class mapped from the process' scheduling priority, we get back the bucket associated with IOPRIO_CLASS_BE. Now, there is no queue allocated there yet, so we create it: cfqq = cfq_find_alloc_queue(cfqd, is_sync, cic, bio, gfp_mask); That function ends up doing this: cfq_init_cfqq(cfqd, cfqq, current->pid, is_sync); cfq_init_prio_data(cfqq, cic); cfq_init_cfqq marks the priority as having changed. Then, cfq_init_prio data does this: ioprio_class = IOPRIO_PRIO_CLASS(cic->ioprio); switch (ioprio_class) { default: printk(KERN_ERR "cfq: bad prio %x\n", ioprio_class); case IOPRIO_CLASS_NONE: /* * no prio set, inherit CPU scheduling settings */ cfqq->ioprio = task_nice_ioprio(tsk); cfqq->ioprio_class = task_nice_ioclass(tsk); break; So we basically have two code paths that treat IOPRIO_CLASS_NONE differently, which results in an RT async cfqq filed into a best effort bucket. Attached is a patch which fixes the problem. I'm not sure how to make it cleaner. Suggestions would be welcome. Signed-off-by: Jeff Moyer Tested-by: Hidehiro Kawai Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/cfq-iosched.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 01898a44709efd..5da8e6e9ab4bfd 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -3661,12 +3661,17 @@ static struct cfq_queue * cfq_get_queue(struct cfq_data *cfqd, bool is_sync, struct cfq_io_cq *cic, struct bio *bio, gfp_t gfp_mask) { - const int ioprio_class = IOPRIO_PRIO_CLASS(cic->ioprio); - const int ioprio = IOPRIO_PRIO_DATA(cic->ioprio); + int ioprio_class = IOPRIO_PRIO_CLASS(cic->ioprio); + int ioprio = IOPRIO_PRIO_DATA(cic->ioprio); struct cfq_queue **async_cfqq = NULL; struct cfq_queue *cfqq = NULL; if (!is_sync) { + if (!ioprio_valid(cic->ioprio)) { + struct task_struct *tsk = current; + ioprio = task_nice_ioprio(tsk); + ioprio_class = task_nice_ioclass(tsk); + } async_cfqq = cfq_async_queue_prio(cfqd, ioprio_class, ioprio); cfqq = *async_cfqq; } From dd8ef93c99b2df301641f15bde5b8f1bea3916f1 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Wed, 11 Feb 2015 14:46:37 -0500 Subject: [PATCH 108/788] cipso: don't use IPCB() to locate the CIPSO IP option commit 04f81f0154e4bf002be6f4d85668ce1257efa4d9 upstream. Using the IPCB() macro to get the IPv4 options is convenient, but unfortunately NetLabel often needs to examine the CIPSO option outside of the scope of the IP layer in the stack. While historically IPCB() worked above the IP layer, due to the inclusion of the inet_skb_param struct at the head of the {tcp,udp}_skb_cb structs, recent commit 971f10ec ("tcp: better TCP_SKB_CB layout to reduce cache line misses") reordered the tcp_skb_cb struct and invalidated this IPCB() trick. This patch fixes the problem by creating a new function, cipso_v4_optptr(), which locates the CIPSO option inside the IP header without calling IPCB(). Unfortunately, this isn't as fast as a simple lookup so some additional tweaks were made to limit the use of this new function. Reported-by: Casey Schaufler Signed-off-by: Paul Moore Tested-by: Casey Schaufler Signed-off-by: Greg Kroah-Hartman --- include/net/cipso_ipv4.h | 25 +++++++++++------- net/ipv4/cipso_ipv4.c | 51 +++++++++++++++++++++--------------- net/netlabel/netlabel_kapi.c | 15 +++++++---- 3 files changed, 56 insertions(+), 35 deletions(-) diff --git a/include/net/cipso_ipv4.h b/include/net/cipso_ipv4.h index a6fd939f202d39..3ebb168b9afc68 100644 --- a/include/net/cipso_ipv4.h +++ b/include/net/cipso_ipv4.h @@ -120,13 +120,6 @@ extern int cipso_v4_rbm_optfmt; extern int cipso_v4_rbm_strictvalid; #endif -/* - * Helper Functions - */ - -#define CIPSO_V4_OPTEXIST(x) (IPCB(x)->opt.cipso != 0) -#define CIPSO_V4_OPTPTR(x) (skb_network_header(x) + IPCB(x)->opt.cipso) - /* * DOI List Functions */ @@ -190,7 +183,7 @@ static inline int cipso_v4_doi_domhsh_remove(struct cipso_v4_doi *doi_def, #ifdef CONFIG_NETLABEL void cipso_v4_cache_invalidate(void); -int cipso_v4_cache_add(const struct sk_buff *skb, +int cipso_v4_cache_add(const unsigned char *cipso_ptr, const struct netlbl_lsm_secattr *secattr); #else static inline void cipso_v4_cache_invalidate(void) @@ -198,7 +191,7 @@ static inline void cipso_v4_cache_invalidate(void) return; } -static inline int cipso_v4_cache_add(const struct sk_buff *skb, +static inline int cipso_v4_cache_add(const unsigned char *cipso_ptr, const struct netlbl_lsm_secattr *secattr) { return 0; @@ -211,6 +204,8 @@ static inline int cipso_v4_cache_add(const struct sk_buff *skb, #ifdef CONFIG_NETLABEL void cipso_v4_error(struct sk_buff *skb, int error, u32 gateway); +int cipso_v4_getattr(const unsigned char *cipso, + struct netlbl_lsm_secattr *secattr); int cipso_v4_sock_setattr(struct sock *sk, const struct cipso_v4_doi *doi_def, const struct netlbl_lsm_secattr *secattr); @@ -226,6 +221,7 @@ int cipso_v4_skbuff_setattr(struct sk_buff *skb, int cipso_v4_skbuff_delattr(struct sk_buff *skb); int cipso_v4_skbuff_getattr(const struct sk_buff *skb, struct netlbl_lsm_secattr *secattr); +unsigned char *cipso_v4_optptr(const struct sk_buff *skb); int cipso_v4_validate(const struct sk_buff *skb, unsigned char **option); #else static inline void cipso_v4_error(struct sk_buff *skb, @@ -235,6 +231,12 @@ static inline void cipso_v4_error(struct sk_buff *skb, return; } +static inline int cipso_v4_getattr(const unsigned char *cipso, + struct netlbl_lsm_secattr *secattr) +{ + return -ENOSYS; +} + static inline int cipso_v4_sock_setattr(struct sock *sk, const struct cipso_v4_doi *doi_def, const struct netlbl_lsm_secattr *secattr) @@ -282,6 +284,11 @@ static inline int cipso_v4_skbuff_getattr(const struct sk_buff *skb, return -ENOSYS; } +static inline unsigned char *cipso_v4_optptr(const struct sk_buff *skb) +{ + return NULL; +} + static inline int cipso_v4_validate(const struct sk_buff *skb, unsigned char **option) { diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c index 5160c710f2eb4d..e361ea6f3fc8ce 100644 --- a/net/ipv4/cipso_ipv4.c +++ b/net/ipv4/cipso_ipv4.c @@ -378,20 +378,18 @@ static int cipso_v4_cache_check(const unsigned char *key, * negative values on failure. * */ -int cipso_v4_cache_add(const struct sk_buff *skb, +int cipso_v4_cache_add(const unsigned char *cipso_ptr, const struct netlbl_lsm_secattr *secattr) { int ret_val = -EPERM; u32 bkt; struct cipso_v4_map_cache_entry *entry = NULL; struct cipso_v4_map_cache_entry *old_entry = NULL; - unsigned char *cipso_ptr; u32 cipso_ptr_len; if (!cipso_v4_cache_enabled || cipso_v4_cache_bucketsize <= 0) return 0; - cipso_ptr = CIPSO_V4_OPTPTR(skb); cipso_ptr_len = cipso_ptr[1]; entry = kzalloc(sizeof(*entry), GFP_ATOMIC); @@ -1578,6 +1576,33 @@ static int cipso_v4_parsetag_loc(const struct cipso_v4_doi *doi_def, return 0; } +/** + * cipso_v4_optptr - Find the CIPSO option in the packet + * @skb: the packet + * + * Description: + * Parse the packet's IP header looking for a CIPSO option. Returns a pointer + * to the start of the CIPSO option on success, NULL if one if not found. + * + */ +unsigned char *cipso_v4_optptr(const struct sk_buff *skb) +{ + const struct iphdr *iph = ip_hdr(skb); + unsigned char *optptr = (unsigned char *)&(ip_hdr(skb)[1]); + int optlen; + int taglen; + + for (optlen = iph->ihl*4 - sizeof(struct iphdr); optlen > 0; ) { + if (optptr[0] == IPOPT_CIPSO) + return optptr; + taglen = optptr[1]; + optlen -= taglen; + optptr += taglen; + } + + return NULL; +} + /** * cipso_v4_validate - Validate a CIPSO option * @option: the start of the option, on error it is set to point to the error @@ -2119,8 +2144,8 @@ void cipso_v4_req_delattr(struct request_sock *req) * on success and negative values on failure. * */ -static int cipso_v4_getattr(const unsigned char *cipso, - struct netlbl_lsm_secattr *secattr) +int cipso_v4_getattr(const unsigned char *cipso, + struct netlbl_lsm_secattr *secattr) { int ret_val = -ENOMSG; u32 doi; @@ -2305,22 +2330,6 @@ int cipso_v4_skbuff_delattr(struct sk_buff *skb) return 0; } -/** - * cipso_v4_skbuff_getattr - Get the security attributes from the CIPSO option - * @skb: the packet - * @secattr: the security attributes - * - * Description: - * Parse the given packet's CIPSO option and return the security attributes. - * Returns zero on success and negative values on failure. - * - */ -int cipso_v4_skbuff_getattr(const struct sk_buff *skb, - struct netlbl_lsm_secattr *secattr) -{ - return cipso_v4_getattr(CIPSO_V4_OPTPTR(skb), secattr); -} - /* * Setup Functions */ diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c index a845cd4cf21e5f..28cddc85b7005a 100644 --- a/net/netlabel/netlabel_kapi.c +++ b/net/netlabel/netlabel_kapi.c @@ -1065,10 +1065,12 @@ int netlbl_skbuff_getattr(const struct sk_buff *skb, u16 family, struct netlbl_lsm_secattr *secattr) { + unsigned char *ptr; + switch (family) { case AF_INET: - if (CIPSO_V4_OPTEXIST(skb) && - cipso_v4_skbuff_getattr(skb, secattr) == 0) + ptr = cipso_v4_optptr(skb); + if (ptr && cipso_v4_getattr(ptr, secattr) == 0) return 0; break; #if IS_ENABLED(CONFIG_IPV6) @@ -1094,7 +1096,7 @@ int netlbl_skbuff_getattr(const struct sk_buff *skb, */ void netlbl_skbuff_err(struct sk_buff *skb, int error, int gateway) { - if (CIPSO_V4_OPTEXIST(skb)) + if (cipso_v4_optptr(skb)) cipso_v4_error(skb, error, gateway); } @@ -1126,11 +1128,14 @@ void netlbl_cache_invalidate(void) int netlbl_cache_add(const struct sk_buff *skb, const struct netlbl_lsm_secattr *secattr) { + unsigned char *ptr; + if ((secattr->flags & NETLBL_SECATTR_CACHE) == 0) return -ENOMSG; - if (CIPSO_V4_OPTEXIST(skb)) - return cipso_v4_cache_add(skb, secattr); + ptr = cipso_v4_optptr(skb); + if (ptr) + return cipso_v4_cache_add(ptr, secattr); return -ENOMSG; } From 29a4446846408618de32c378a6378db609d9f486 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Tue, 10 Feb 2015 22:14:53 -0500 Subject: [PATCH 109/788] ring-buffer: Do not wake up a splice waiter when page is not full commit 1e0d6714aceb770b04161fbedd7765d0e1fc27bd upstream. When an application connects to the ring buffer via splice, it can only read full pages. Splice does not work with partial pages. If there is not enough data to fill a page, the splice command will either block or return -EAGAIN (if set to nonblock). Code was added where if the page is not full, to just sleep again. The problem is, it will get woken up again on the next event. That is, when something is written into the ring buffer, if there is a waiter it will wake it up. The waiter would then check the buffer, see that it still does not have enough data to fill a page and go back to sleep. To make matters worse, when the waiter goes back to sleep, it could cause another event, which would wake it back up again to see it doesn't have enough data and sleep again. This produces a tremendous overhead and fills the ring buffer with noise. For example, recording sched_switch on an idle system for 10 seconds produces 25,350,475 events!!! Create another wait queue for those waiters wanting full pages. When an event is written, it only wakes up waiters if there's a full page of data. It does not wake up the waiter if the page is not yet full. After this change, recording sched_switch on an idle system for 10 seconds produces only 800 events. Getting rid of 25,349,675 useless events (99.9969% of events!!), is something to take seriously. Cc: Rabin Vincent Fixes: e30f53aad220 "tracing: Do not busy wait in buffer splice" Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- kernel/trace/ring_buffer.c | 40 +++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 7a4104cb95cb28..d2e151c83bd5ef 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -447,7 +447,10 @@ int ring_buffer_print_page_header(struct trace_seq *s) struct rb_irq_work { struct irq_work work; wait_queue_head_t waiters; + wait_queue_head_t full_waiters; bool waiters_pending; + bool full_waiters_pending; + bool wakeup_full; }; /* @@ -529,6 +532,10 @@ static void rb_wake_up_waiters(struct irq_work *work) struct rb_irq_work *rbwork = container_of(work, struct rb_irq_work, work); wake_up_all(&rbwork->waiters); + if (rbwork->wakeup_full) { + rbwork->wakeup_full = false; + wake_up_all(&rbwork->full_waiters); + } } /** @@ -553,9 +560,11 @@ int ring_buffer_wait(struct ring_buffer *buffer, int cpu, bool full) * data in any cpu buffer, or a specific buffer, put the * caller on the appropriate wait queue. */ - if (cpu == RING_BUFFER_ALL_CPUS) + if (cpu == RING_BUFFER_ALL_CPUS) { work = &buffer->irq_work; - else { + /* Full only makes sense on per cpu reads */ + full = false; + } else { if (!cpumask_test_cpu(cpu, buffer->cpumask)) return -ENODEV; cpu_buffer = buffer->buffers[cpu]; @@ -564,7 +573,10 @@ int ring_buffer_wait(struct ring_buffer *buffer, int cpu, bool full) while (true) { - prepare_to_wait(&work->waiters, &wait, TASK_INTERRUPTIBLE); + if (full) + prepare_to_wait(&work->full_waiters, &wait, TASK_INTERRUPTIBLE); + else + prepare_to_wait(&work->waiters, &wait, TASK_INTERRUPTIBLE); /* * The events can happen in critical sections where @@ -586,7 +598,10 @@ int ring_buffer_wait(struct ring_buffer *buffer, int cpu, bool full) * that is necessary is that the wake up happens after * a task has been queued. It's OK for spurious wake ups. */ - work->waiters_pending = true; + if (full) + work->full_waiters_pending = true; + else + work->waiters_pending = true; if (signal_pending(current)) { ret = -EINTR; @@ -615,7 +630,10 @@ int ring_buffer_wait(struct ring_buffer *buffer, int cpu, bool full) schedule(); } - finish_wait(&work->waiters, &wait); + if (full) + finish_wait(&work->full_waiters, &wait); + else + finish_wait(&work->waiters, &wait); return ret; } @@ -1230,6 +1248,7 @@ rb_allocate_cpu_buffer(struct ring_buffer *buffer, int nr_pages, int cpu) init_completion(&cpu_buffer->update_done); init_irq_work(&cpu_buffer->irq_work.work, rb_wake_up_waiters); init_waitqueue_head(&cpu_buffer->irq_work.waiters); + init_waitqueue_head(&cpu_buffer->irq_work.full_waiters); bpage = kzalloc_node(ALIGN(sizeof(*bpage), cache_line_size()), GFP_KERNEL, cpu_to_node(cpu)); @@ -2801,6 +2820,8 @@ static void rb_commit(struct ring_buffer_per_cpu *cpu_buffer, static __always_inline void rb_wakeups(struct ring_buffer *buffer, struct ring_buffer_per_cpu *cpu_buffer) { + bool pagebusy; + if (buffer->irq_work.waiters_pending) { buffer->irq_work.waiters_pending = false; /* irq_work_queue() supplies it's own memory barriers */ @@ -2812,6 +2833,15 @@ rb_wakeups(struct ring_buffer *buffer, struct ring_buffer_per_cpu *cpu_buffer) /* irq_work_queue() supplies it's own memory barriers */ irq_work_queue(&cpu_buffer->irq_work.work); } + + pagebusy = cpu_buffer->reader_page == cpu_buffer->commit_page; + + if (!pagebusy && cpu_buffer->irq_work.full_waiters_pending) { + cpu_buffer->irq_work.wakeup_full = true; + cpu_buffer->irq_work.full_waiters_pending = false; + /* irq_work_queue() supplies it's own memory barriers */ + irq_work_queue(&cpu_buffer->irq_work.work); + } } /** From 13f431faed6473bf0440e918f1e090107cf708ae Mon Sep 17 00:00:00 2001 From: Andrey Ryabinin Date: Tue, 13 Jan 2015 18:52:40 +0300 Subject: [PATCH 110/788] smack: fix possible use after frees in task_security() callers commit 6d1cff2a885850b78b40c34777b46cf5da5d1050 upstream. We hit use after free on dereferncing pointer to task_smack struct in smk_of_task() called from smack_task_to_inode(). task_security() macro uses task_cred_xxx() to get pointer to the task_smack. task_cred_xxx() could be used only for non-pointer members of task's credentials. It cannot be used for pointer members since what they point to may disapper after dropping RCU read lock. Mainly task_security() used this way: smk_of_task(task_security(p)) Intead of this introduce function smk_of_task_struct() which takes task_struct as argument and returns pointer to smk_known struct and do this under RCU read lock. Bogus task_security() macro is not used anymore, so remove it. KASan's report for this: AddressSanitizer: use after free in smack_task_to_inode+0x50/0x70 at addr c4635600 ============================================================================= BUG kmalloc-64 (Tainted: PO): kasan error ----------------------------------------------------------------------------- Disabling lock debugging due to kernel taint INFO: Allocated in new_task_smack+0x44/0xd8 age=39 cpu=0 pid=1866 kmem_cache_alloc_trace+0x88/0x1bc new_task_smack+0x44/0xd8 smack_cred_prepare+0x48/0x21c security_prepare_creds+0x44/0x4c prepare_creds+0xdc/0x110 smack_setprocattr+0x104/0x150 security_setprocattr+0x4c/0x54 proc_pid_attr_write+0x12c/0x194 vfs_write+0x1b0/0x370 SyS_write+0x5c/0x94 ret_fast_syscall+0x0/0x48 INFO: Freed in smack_cred_free+0xc4/0xd0 age=27 cpu=0 pid=1564 kfree+0x270/0x290 smack_cred_free+0xc4/0xd0 security_cred_free+0x34/0x3c put_cred_rcu+0x58/0xcc rcu_process_callbacks+0x738/0x998 __do_softirq+0x264/0x4cc do_softirq+0x94/0xf4 irq_exit+0xbc/0x120 handle_IRQ+0x104/0x134 gic_handle_irq+0x70/0xac __irq_svc+0x44/0x78 _raw_spin_unlock+0x18/0x48 sync_inodes_sb+0x17c/0x1d8 sync_filesystem+0xac/0xfc vdfs_file_fsync+0x90/0xc0 vfs_fsync_range+0x74/0x7c INFO: Slab 0xd3b23f50 objects=32 used=31 fp=0xc4635600 flags=0x4080 INFO: Object 0xc4635600 @offset=5632 fp=0x (null) Bytes b4 c46355f0: 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a ZZZZZZZZZZZZZZZZ Object c4635600: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b kkkkkkkkkkkkkkkk Object c4635610: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b kkkkkkkkkkkkkkkk Object c4635620: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b kkkkkkkkkkkkkkkk Object c4635630: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b a5 kkkkkkkkkkkkkkk. Redzone c4635640: bb bb bb bb .... Padding c46356e8: 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a ZZZZZZZZZZZZZZZZ Padding c46356f8: 5a 5a 5a 5a 5a 5a 5a 5a ZZZZZZZZ CPU: 5 PID: 834 Comm: launchpad_prelo Tainted: PBO 3.10.30 #1 Backtrace: [] (dump_backtrace+0x0/0x158) from [] (show_stack+0x20/0x24) r7:c4634010 r6:d3b23f50 r5:c4635600 r4:d1002140 [] (show_stack+0x0/0x24) from [] (dump_stack+0x20/0x28) [] (dump_stack+0x0/0x28) from [] (print_trailer+0x124/0x144) [] (print_trailer+0x0/0x144) from [] (object_err+0x3c/0x44) r7:c4635600 r6:d1002140 r5:d3b23f50 r4:c4635600 [] (object_err+0x0/0x44) from [] (kasan_report_error+0x2b8/0x538) r6:d1002140 r5:d3b23f50 r4:c6429cf8 r3:c09e1aa7 [] (kasan_report_error+0x0/0x538) from [] (__asan_load4+0xd4/0xf8) [] (__asan_load4+0x0/0xf8) from [] (smack_task_to_inode+0x50/0x70) r5:c4635600 r4:ca9da000 [] (smack_task_to_inode+0x0/0x70) from [] (security_task_to_inode+0x3c/0x44) r5:cca25e80 r4:c0ba9780 [] (security_task_to_inode+0x0/0x44) from [] (pid_revalidate+0x124/0x178) r6:00000000 r5:cca25e80 r4:cbabe3c0 r3:00008124 [] (pid_revalidate+0x0/0x178) from [] (lookup_fast+0x35c/0x43y4) r9:c6429efc r8:00000101 r7:c079d940 r6:c6429e90 r5:c6429ed8 r4:c83c4148 [] (lookup_fast+0x0/0x434) from [] (do_last.isra.24+0x1c0/0x1108) [] (do_last.isra.24+0x0/0x1108) from [] (path_openat.isra.25+0xf4/0x648) [] (path_openat.isra.25+0x0/0x648) from [] (do_filp_open+0x3c/0x88) [] (do_filp_open+0x0/0x88) from [] (do_sys_open+0xf0/0x198) r7:00000001 r6:c0ea2180 r5:0000000b r4:00000000 [] (do_sys_open+0x0/0x198) from [] (SyS_open+0x30/0x34) [] (SyS_open+0x0/0x34) from [] (ret_fast_syscall+0x0/0x48) Read of size 4 by thread T834: Memory state around the buggy address: c4635380: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc c4635400: 00 00 00 00 00 00 00 00 fc fc fc fc fc fc fc fc c4635480: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc c4635500: 00 00 00 00 00 fc fc fc fc fc fc fc fc fc fc fc c4635580: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc >c4635600: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ^ c4635680: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb c4635700: 00 00 00 00 04 fc fc fc fc fc fc fc fc fc fc fc c4635780: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc c4635800: 00 00 00 00 00 00 04 fc fc fc fc fc fc fc fc fc c4635880: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ================================================================== Signed-off-by: Andrey Ryabinin Signed-off-by: Greg Kroah-Hartman --- security/smack/smack.h | 10 ++++++++++ security/smack/smack_lsm.c | 24 +++++++++++++----------- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/security/smack/smack.h b/security/smack/smack.h index b828a379377c9c..b48359c0da32cb 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -298,6 +298,16 @@ static inline struct smack_known *smk_of_task(const struct task_smack *tsp) return tsp->smk_task; } +static inline struct smack_known *smk_of_task_struct(const struct task_struct *t) +{ + struct smack_known *skp; + + rcu_read_lock(); + skp = smk_of_task(__task_cred(t)->security); + rcu_read_unlock(); + return skp; +} + /* * Present a pointer to the forked smack label entry in an task blob. */ diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index f1b17a476e12d1..a7178773dde77d 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -43,8 +43,6 @@ #include #include "smack.h" -#define task_security(task) (task_cred_xxx((task), security)) - #define TRANS_TRUE "TRUE" #define TRANS_TRUE_SIZE 4 @@ -120,7 +118,7 @@ static int smk_bu_current(char *note, struct smack_known *oskp, static int smk_bu_task(struct task_struct *otp, int mode, int rc) { struct task_smack *tsp = current_security(); - struct task_smack *otsp = task_security(otp); + struct smack_known *smk_task = smk_of_task_struct(otp); char acc[SMK_NUM_ACCESS_TYPE + 1]; if (rc <= 0) @@ -128,7 +126,7 @@ static int smk_bu_task(struct task_struct *otp, int mode, int rc) smk_bu_mode(mode, acc); pr_info("Smack Bringup: (%s %s %s) %s to %s\n", - tsp->smk_task->smk_known, otsp->smk_task->smk_known, acc, + tsp->smk_task->smk_known, smk_task->smk_known, acc, current->comm, otp->comm); return 0; } @@ -345,7 +343,8 @@ static int smk_ptrace_rule_check(struct task_struct *tracer, saip = &ad; } - tsp = task_security(tracer); + rcu_read_lock(); + tsp = __task_cred(tracer)->security; tracer_known = smk_of_task(tsp); if ((mode & PTRACE_MODE_ATTACH) && @@ -365,11 +364,14 @@ static int smk_ptrace_rule_check(struct task_struct *tracer, tracee_known->smk_known, 0, rc, saip); + rcu_read_unlock(); return rc; } /* In case of rule==SMACK_PTRACE_DEFAULT or mode==PTRACE_MODE_READ */ rc = smk_tskacc(tsp, tracee_known, smk_ptrace_mode(mode), saip); + + rcu_read_unlock(); return rc; } @@ -396,7 +398,7 @@ static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode) if (rc != 0) return rc; - skp = smk_of_task(task_security(ctp)); + skp = smk_of_task_struct(ctp); rc = smk_ptrace_rule_check(current, skp, mode, __func__); return rc; @@ -1826,7 +1828,7 @@ static int smk_curacc_on_task(struct task_struct *p, int access, const char *caller) { struct smk_audit_info ad; - struct smack_known *skp = smk_of_task(task_security(p)); + struct smack_known *skp = smk_of_task_struct(p); int rc; smk_ad_init(&ad, caller, LSM_AUDIT_DATA_TASK); @@ -1879,7 +1881,7 @@ static int smack_task_getsid(struct task_struct *p) */ static void smack_task_getsecid(struct task_struct *p, u32 *secid) { - struct smack_known *skp = smk_of_task(task_security(p)); + struct smack_known *skp = smk_of_task_struct(p); *secid = skp->smk_secid; } @@ -1986,7 +1988,7 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info, { struct smk_audit_info ad; struct smack_known *skp; - struct smack_known *tkp = smk_of_task(task_security(p)); + struct smack_known *tkp = smk_of_task_struct(p); int rc; smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); @@ -2040,7 +2042,7 @@ static int smack_task_wait(struct task_struct *p) static void smack_task_to_inode(struct task_struct *p, struct inode *inode) { struct inode_smack *isp = inode->i_security; - struct smack_known *skp = smk_of_task(task_security(p)); + struct smack_known *skp = smk_of_task_struct(p); isp->smk_inode = skp; } @@ -3200,7 +3202,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) */ static int smack_getprocattr(struct task_struct *p, char *name, char **value) { - struct smack_known *skp = smk_of_task(task_security(p)); + struct smack_known *skp = smk_of_task_struct(p); char *cp; int slen; From f30f3239d917ff0e16e485c903113ab4fa42c813 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 7 Jan 2015 18:04:18 +0200 Subject: [PATCH 111/788] axonram: Fix bug in direct_access commit 91117a20245b59f70b563523edbf998a62fc6383 upstream. The 'pfn' returned by axonram was completely bogus, and has been since 2008. Signed-off-by: Matthew Wilcox Reviewed-by: Jan Kara Reviewed-by: Mathieu Desnoyers Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/sysdev/axonram.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/sysdev/axonram.c b/arch/powerpc/sysdev/axonram.c index f532c92bf99dab..367533bb3d48c0 100644 --- a/arch/powerpc/sysdev/axonram.c +++ b/arch/powerpc/sysdev/axonram.c @@ -156,7 +156,7 @@ axon_ram_direct_access(struct block_device *device, sector_t sector, } *kaddr = (void *)(bank->ph_addr + offset); - *pfn = virt_to_phys(kaddr) >> PAGE_SHIFT; + *pfn = virt_to_phys(*kaddr) >> PAGE_SHIFT; return 0; } From e8c9b752b91051a42960f9ef84ffa40450b9a408 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Tue, 30 Dec 2014 07:11:11 -0500 Subject: [PATCH 112/788] tty: Remove warning in tty_lock_slave() commit eef15e2a54fad4c2ce3f0a81485dc591ce678f4f upstream. Commit 2aff5e2bc62db43e05c814461a08aff0fc2b7fe5 ('tty: Change tty lock order to master->slave') added a warning which is broken and unnecessary now that the tty lock has fixed lock subclasses, added in commit 2febdb632bb96235b94b8fccaf882a78f8f4b2bb ('tty: Preset lock subclass for nested tty locks'). Reported-by: Dan Carpenter Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_mutex.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/tty/tty_mutex.c b/drivers/tty/tty_mutex.c index 4486741190c47d..a872389dc0bcd8 100644 --- a/drivers/tty/tty_mutex.c +++ b/drivers/tty/tty_mutex.c @@ -46,12 +46,8 @@ EXPORT_SYMBOL(tty_unlock); void __lockfunc tty_lock_slave(struct tty_struct *tty) { - if (tty && tty != tty->link) { - WARN_ON(!mutex_is_locked(&tty->link->legacy_mutex) || - !tty->driver->type == TTY_DRIVER_TYPE_PTY || - !tty->driver->type == PTY_TYPE_SLAVE); + if (tty && tty != tty->link) tty_lock(tty); - } } void __lockfunc tty_unlock_slave(struct tty_struct *tty) From cb7289785821ad7624f61760bc9825c421831aba Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 19 Jan 2015 13:05:03 -0500 Subject: [PATCH 113/788] tty: Prevent untrappable signals from malicious program commit 37480a05685ed5b8e1b9bf5e5c53b5810258b149 upstream. Commit 26df6d13406d1a5 ("tty: Add EXTPROC support for LINEMODE") allows a process which has opened a pty master to send _any_ signal to the process group of the pty slave. Although potentially exploitable by a malicious program running a setuid program on a pty slave, it's unknown if this exploit currently exists. Limit to signals actually used. Cc: Theodore Ts'o Cc: Howard Chu Cc: One Thousand Gnomes Cc: Jiri Slaby Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index a9d256d6e909c1..6e1f1505f04e4b 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -210,6 +210,9 @@ static int pty_signal(struct tty_struct *tty, int sig) { struct pid *pgrp; + if (sig != SIGINT && sig != SIGQUIT && sig != SIGTSTP) + return -EINVAL; + if (tty->link) { pgrp = tty_get_pgrp(tty->link); if (pgrp) From 36ed10d9764d2536f1a9927d7b6ac16703baa2dd Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Tue, 9 Dec 2014 14:31:32 +0100 Subject: [PATCH 114/788] tty/serial: at91: use correct type for dma_sync_*_for_cpu() and dma_sync_*_for_device() commit 485819b5b9ed82372dacae775998f3c33717f7fe upstream. dma_sync_*_for_cpu() and dma_sync_*_for_device() use 'enum dma_data_direction', not 'enum dma_transfer_direction' Signed-off-by: Cyrille Pitchen Acked-by: Nicolas Ferre Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 4d848a29e22345..756f567527c403 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -794,7 +794,7 @@ static void atmel_tx_dma(struct uart_port *port) return; } - dma_sync_sg_for_device(port->dev, sg, 1, DMA_MEM_TO_DEV); + dma_sync_sg_for_device(port->dev, sg, 1, DMA_TO_DEVICE); atmel_port->desc_tx = desc; desc->callback = atmel_complete_tx_dma; @@ -927,7 +927,7 @@ static void atmel_rx_from_dma(struct uart_port *port) dma_sync_sg_for_cpu(port->dev, &atmel_port->sg_rx, 1, - DMA_DEV_TO_MEM); + DMA_FROM_DEVICE); /* * ring->head points to the end of data already written by the DMA. @@ -974,7 +974,7 @@ static void atmel_rx_from_dma(struct uart_port *port) dma_sync_sg_for_device(port->dev, &atmel_port->sg_rx, 1, - DMA_DEV_TO_MEM); + DMA_FROM_DEVICE); /* * Drop the lock here since it might end up calling From 8155e4a095ba26d5e0204deb3f97aca2296fae5e Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Tue, 9 Dec 2014 14:31:33 +0100 Subject: [PATCH 115/788] tty/serial: at91: enable peripheral clock before accessing I/O registers commit d4f641876a68d1961e30c202709cc2d484f69f6f upstream. atmel_serial_probe() calls atmel_init_port(). In turn, atmel_init_port() calls clk_disable_unprepare() to disable the peripheral clock before returning. Later atmel_serial_probe() accesses some I/O registers such as the Mode and Control registers for RS485 support then the Name and Version registers, through a call to atmel_get_ip_name(), but at that moment the peripheral clock was still disabled. Signed-off-by: Cyrille Pitchen Acked-by: Nicolas Ferre Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 756f567527c403..c6621dc296c041 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -2596,6 +2596,12 @@ static int atmel_serial_probe(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 1); platform_set_drvdata(pdev, port); + /* + * The peripheral clock has been disabled by atmel_init_port(): + * enable it before accessing I/O registers + */ + clk_prepare_enable(port->clk); + if (rs485_enabled) { UART_PUT_MR(&port->uart, ATMEL_US_USMODE_NORMAL); UART_PUT_CR(&port->uart, ATMEL_US_RTSEN); @@ -2606,6 +2612,12 @@ static int atmel_serial_probe(struct platform_device *pdev) */ atmel_get_ip_name(&port->uart); + /* + * The peripheral clock can now safely be disabled till the port + * is used + */ + clk_disable_unprepare(port->clk); + return 0; err_add_port: From f047b326ceb5c86e0685a7a4f310856ccce899c9 Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Tue, 9 Dec 2014 14:31:34 +0100 Subject: [PATCH 116/788] tty/serial: at91: fix error handling in atmel_serial_probe() commit 6fbb9bdf0f3fbe23aeff806489791aa876adaffb upstream. -EDEFER error wasn't handle properly by atmel_serial_probe(). As an example, when atmel_serial_probe() is called for the first time, we pass the test_and_set_bit() test to check whether the port has already been initalized. Then we call atmel_init_port(), which may return -EDEFER, possibly returned before by clk_get(). Consequently atmel_serial_probe() used to return this error code WITHOUT clearing the port bit in the "atmel_ports_in_use" mask. When atmel_serial_probe() was called for the second time, it used to fail on the test_and_set_bit() function then returning -EBUSY. When atmel_serial_probe() fails, this patch make it clear the port bit in the "atmel_ports_in_use" mask, if needed, before returning the error code. Signed-off-by: Cyrille Pitchen Acked-by: Nicolas Ferre Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index c6621dc296c041..92a8b263735a6f 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -2565,7 +2565,7 @@ static int atmel_serial_probe(struct platform_device *pdev) ret = atmel_init_port(port, pdev); if (ret) - goto err; + goto err_clear_bit; if (!atmel_use_pdc_rx(&port->uart)) { ret = -ENOMEM; @@ -2628,6 +2628,8 @@ static int atmel_serial_probe(struct platform_device *pdev) clk_put(port->clk); port->clk = NULL; } +err_clear_bit: + clear_bit(port->uart.line, atmel_ports_in_use); err: return ret; } From 6766138fa1a1c4142a5242c2bd4226b72e086c54 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 25 Jan 2015 23:45:27 +0200 Subject: [PATCH 117/788] mei: mask interrupt set bit on clean reset bit commit 1ab1e79b9fd4b01331490bbe2e630a0fc0b25449 upstream. We should mask interrupt set bit when writing back hcsr value in reset bit clean-up. This is refinement for mei: clean reset bit before reset commit b13a65ef190e488e2761d65bdd2e1fe8a3a125f5 Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 06ff0a2ec96071..ccc1b405ca212c 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -242,7 +242,7 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable) if ((hcsr & H_RST) == H_RST) { dev_warn(dev->dev, "H_RST is set = 0x%08X", hcsr); hcsr &= ~H_RST; - mei_me_reg_write(hw, H_CSR, hcsr); + mei_hcsr_set(hw, hcsr); hcsr = mei_hcsr_read(hw); } From 4cc39e7034f2654c33f75b5e4794e41c568214ec Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 25 Jan 2015 23:45:28 +0200 Subject: [PATCH 118/788] mei: me: release hw from reset only during the reset flow commit 663b7ee9517eec6deea9a48c7a1392a9a34f7809 upstream. We might enter the interrupt handler with hw_ready already set, but prior we actually started the reset flow. To soleve this we move the reset release from the interrupt handler to the HW start wait function which is part of the reset sequence. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index ccc1b405ca212c..f8fd503dfbd69b 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -335,6 +335,7 @@ static int mei_me_hw_ready_wait(struct mei_device *dev) return -ETIME; } + mei_me_hw_reset_release(dev); dev->recvd_hw_ready = false; return 0; } @@ -731,9 +732,7 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) /* check if we need to start the dev */ if (!mei_host_is_ready(dev)) { if (mei_hw_is_ready(dev)) { - mei_me_hw_reset_release(dev); dev_dbg(dev->dev, "we need to start the dev.\n"); - dev->recvd_hw_ready = true; wake_up(&dev->wait_hw_ready); } else { From 19e554a1b79597a9780fc7d273927d89852a3667 Mon Sep 17 00:00:00 2001 From: Lennart Sorensen Date: Wed, 21 Jan 2015 15:24:27 -0500 Subject: [PATCH 119/788] USB: cp210x: add ID for RUGGEDCOM USB Serial Console commit a6f0331236fa75afba14bbcf6668d42cebb55c43 upstream. Added the USB serial console device ID for Siemens Ruggedcom devices which have a USB port for their serial console. Signed-off-by: Len Sorensen Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/cp210x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index f4c56fc1a9f64d..f40c856ff758d9 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -56,6 +56,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x0846, 0x1100) }, /* NetGear Managed Switch M4100 series, M5300 series, M7100 series */ { USB_DEVICE(0x08e6, 0x5501) }, /* Gemalto Prox-PU/CU contactless smartcard reader */ { USB_DEVICE(0x08FD, 0x000A) }, /* Digianswer A/S , ZigBee/802.15.4 MAC Device */ + { USB_DEVICE(0x0908, 0x01FF) }, /* Siemens RUGGEDCOM USB Serial Console */ { USB_DEVICE(0x0BED, 0x1100) }, /* MEI (TM) Cashflow-SC Bill/Voucher Acceptor */ { USB_DEVICE(0x0BED, 0x1101) }, /* MEI series 2000 Combo Acceptor */ { USB_DEVICE(0x0FCF, 0x1003) }, /* Dynastream ANT development board */ From 07cc714778101a93ea971dd9d01a1bff8f00dc94 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 30 Jan 2015 12:58:26 -0500 Subject: [PATCH 120/788] USB: fix use-after-free bug in usb_hcd_unlink_urb() commit c99197902da284b4b723451c1471c45b18537cde upstream. The usb_hcd_unlink_urb() routine in hcd.c contains two possible use-after-free errors. The dev_dbg() statement at the end of the routine dereferences urb and urb->dev even though both structures may have been deallocated. This patch fixes the problem by storing urb->dev in a local variable (avoiding the dereference of urb) and moving the dev_dbg() up before the usb_put_dev() call. Signed-off-by: Alan Stern Reported-by: Joe Lawrence Tested-by: Joe Lawrence Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 11cee55ae39792..45a915ccd71c06 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1618,6 +1618,7 @@ static int unlink1(struct usb_hcd *hcd, struct urb *urb, int status) int usb_hcd_unlink_urb (struct urb *urb, int status) { struct usb_hcd *hcd; + struct usb_device *udev = urb->dev; int retval = -EIDRM; unsigned long flags; @@ -1629,20 +1630,19 @@ int usb_hcd_unlink_urb (struct urb *urb, int status) spin_lock_irqsave(&hcd_urb_unlink_lock, flags); if (atomic_read(&urb->use_count) > 0) { retval = 0; - usb_get_dev(urb->dev); + usb_get_dev(udev); } spin_unlock_irqrestore(&hcd_urb_unlink_lock, flags); if (retval == 0) { hcd = bus_to_hcd(urb->dev->bus); retval = unlink1(hcd, urb, status); - usb_put_dev(urb->dev); + if (retval == 0) + retval = -EINPROGRESS; + else if (retval != -EIDRM && retval != -EBUSY) + dev_dbg(&udev->dev, "hcd_unlink_urb %p fail %d\n", + urb, retval); + usb_put_dev(udev); } - - if (retval == 0) - retval = -EINPROGRESS; - else if (retval != -EIDRM && retval != -EBUSY) - dev_dbg(&urb->dev->dev, "hcd_unlink_urb %p fail %d\n", - urb, retval); return retval; } From 09271f4eea9cd11543ae91db02727a3b20b29d03 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 29 Jan 2015 10:29:18 -0600 Subject: [PATCH 121/788] usb: dwc3: gadget: add missing spin_lock() commit 5c7b3b02de766a8634708953e805399e3544a290 upstream. commit 8e74475b0e0a (usb: dwc3: gadget: use udc-core's reset notifier) added support for the new UDC core's reset notifier to dwc3 but while at it, it removed a spin_lock() from dwc3_reset_gadget() which might cause an unbalanced spin_unlock() further down the line Fixes: 8e74475b0e0a (usb: dwc3: gadget: use udc-core's reset notifier) Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/gadget.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 8f65ab3a3b928f..4efd3bd35586b7 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2043,6 +2043,7 @@ static void dwc3_resume_gadget(struct dwc3 *dwc) if (dwc->gadget_driver && dwc->gadget_driver->resume) { spin_unlock(&dwc->lock); dwc->gadget_driver->resume(&dwc->gadget); + spin_lock(&dwc->lock); } } From e76372f1cc7112d785a9cb7a54ea96f26985ae9e Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 5 Dec 2014 15:13:54 +0100 Subject: [PATCH 122/788] usb: core: buffer: smallest buffer should start at ARCH_DMA_MINALIGN commit 5efd2ea8c9f4f12916ffc8ba636792ce052f6911 upstream. the following error pops up during "testusb -a -t 10" | musb-hdrc musb-hdrc.1.auto: dma_pool_free buffer-128, f134e000/be842000 (bad dma) hcd_buffer_create() creates a few buffers, the smallest has 32 bytes of size. ARCH_KMALLOC_MINALIGN is set to 64 bytes. This combo results in hcd_buffer_alloc() returning memory which is 32 bytes aligned and it might by identified by buffer_offset() as another buffer. This means the buffer which is on a 32 byte boundary will not get freed, instead it tries to free another buffer with the error message. This patch fixes the issue by creating the smallest DMA buffer with the size of ARCH_KMALLOC_MINALIGN (or 32 in case ARCH_KMALLOC_MINALIGN is smaller). This might be 32, 64 or even 128 bytes. The next three pools will have the size 128, 512 and 2048. In case the smallest pool is 128 bytes then we have only three pools instead of four (and zero the first entry in the array). The last pool size is always 2048 bytes which is the assumed PAGE_SIZE / 2 of 4096. I doubt it makes sense to continue using PAGE_SIZE / 2 where we would end up with 8KiB buffer in case we have 16KiB pages. Instead I think it makes sense to have a common size(s) and extend them if there is need to. There is a BUILD_BUG_ON() now in case someone has a minalign of more than 128 bytes. Signed-off-by: Sebastian Andrzej Siewior Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/buffer.c | 26 +++++++++++++++++--------- drivers/usb/core/usb.c | 1 + include/linux/usb/hcd.h | 1 + 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c index 684ef70dc09d2c..506b969ea7fdc9 100644 --- a/drivers/usb/core/buffer.c +++ b/drivers/usb/core/buffer.c @@ -22,17 +22,25 @@ */ /* FIXME tune these based on pool statistics ... */ -static const size_t pool_max[HCD_BUFFER_POOLS] = { - /* platforms without dma-friendly caches might need to - * prevent cacheline sharing... - */ - 32, - 128, - 512, - PAGE_SIZE / 2 - /* bigger --> allocate pages */ +static size_t pool_max[HCD_BUFFER_POOLS] = { + 32, 128, 512, 2048, }; +void __init usb_init_pool_max(void) +{ + /* + * The pool_max values must never be smaller than + * ARCH_KMALLOC_MINALIGN. + */ + if (ARCH_KMALLOC_MINALIGN <= 32) + ; /* Original value is okay */ + else if (ARCH_KMALLOC_MINALIGN <= 64) + pool_max[0] = 64; + else if (ARCH_KMALLOC_MINALIGN <= 128) + pool_max[0] = 0; /* Don't use this pool */ + else + BUILD_BUG(); /* We don't allow this */ +} /* SETUP primitives */ diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 2a92b97f01440d..b1fb9aef0f5b09 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -1049,6 +1049,7 @@ static int __init usb_init(void) pr_info("%s: USB support disabled\n", usbcore_name); return 0; } + usb_init_pool_max(); retval = usb_debugfs_init(); if (retval) diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 086bf13307e6cb..8968f616e414d6 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -453,6 +453,7 @@ extern const struct dev_pm_ops usb_hcd_pci_pm_ops; #endif /* CONFIG_PCI */ /* pci-ish (pdev null is ok) buffer alloc/mapping support */ +void usb_init_pool_max(void); int hcd_buffer_create(struct usb_hcd *hcd); void hcd_buffer_destroy(struct usb_hcd *hcd); From ee31866e8f2fbdc23b9cfedf0b20aa7c605f98f4 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 21 Jan 2015 14:02:43 -0500 Subject: [PATCH 123/788] USB: don't cancel queued resets when unbinding drivers commit 524134d422316a59d5464ccbc12036bbe90c5563 upstream. The USB stack provides a mechanism for drivers to request an asynchronous device reset (usb_queue_reset_device()). The mechanism uses a work item (reset_ws) embedded in the usb_interface structure used by the driver, and the reset is carried out by a work queue routine. The asynchronous reset can race with driver unbinding. When this happens, we try to cancel the queued reset before unbinding the driver, on the theory that the driver won't care about any resets once it is unbound. However, thanks to the fact that lockdep now tracks work queue accesses, this can provoke a lockdep warning in situations where the device reset causes another interface's driver to be unbound; see http://marc.info/?l=linux-usb&m=141893165203776&w=2 for an example. The reason is that the work routine for reset_ws in one interface calls cancel_queued_work() for the reset_ws in another interface. Lockdep thinks this might lead to a work routine trying to cancel itself. The simplest solution is not to cancel queued resets when unbinding drivers. This means we now need to acquire a reference to the usb_interface when queuing a reset_ws work item and to drop the reference when the work routine finishes. We also need to make sure that the usb_interface structure doesn't outlive its parent usb_device; this means acquiring and dropping a reference when the interface is created and destroyed. In addition, cancelling a queued reset can fail (if the device is in the middle of an earlier reset), and this can cause usb_reset_device() to try to rebind an interface that has been deallocated (see http://marc.info/?l=linux-usb&m=142175717016628&w=2 for details). Acquiring the extra references prevents this failure. Signed-off-by: Alan Stern Reported-by: Russell King - ARM Linux Reported-by: Olivier Sobrie Tested-by: Olivier Sobrie Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 17 ----------------- drivers/usb/core/hub.c | 25 +++++++++---------------- drivers/usb/core/message.c | 23 +++-------------------- include/linux/usb.h | 5 ----- 4 files changed, 12 insertions(+), 58 deletions(-) diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 874dec31a11195..c76ec9758ce301 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -275,21 +275,6 @@ static int usb_unbind_device(struct device *dev) return 0; } -/* - * Cancel any pending scheduled resets - * - * [see usb_queue_reset_device()] - * - * Called after unconfiguring / when releasing interfaces. See - * comments in __usb_queue_reset_device() regarding - * udev->reset_running. - */ -static void usb_cancel_queued_reset(struct usb_interface *iface) -{ - if (iface->reset_running == 0) - cancel_work_sync(&iface->reset_ws); -} - /* called from driver core with dev locked */ static int usb_probe_interface(struct device *dev) { @@ -380,7 +365,6 @@ static int usb_probe_interface(struct device *dev) usb_set_intfdata(intf, NULL); intf->needs_remote_wakeup = 0; intf->condition = USB_INTERFACE_UNBOUND; - usb_cancel_queued_reset(intf); /* If the LPM disable succeeded, balance the ref counts. */ if (!lpm_disable_error) @@ -425,7 +409,6 @@ static int usb_unbind_interface(struct device *dev) usb_disable_interface(udev, intf, false); driver->disconnect(intf); - usb_cancel_queued_reset(intf); /* Free streams */ for (i = 0, j = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) { diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index aeb50bb6ba9ca1..b4bfa3ac4b1220 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -5589,26 +5589,19 @@ EXPORT_SYMBOL_GPL(usb_reset_device); * possible; depending on how the driver attached to each interface * handles ->pre_reset(), the second reset might happen or not. * - * - If a driver is unbound and it had a pending reset, the reset will - * be cancelled. + * - If the reset is delayed so long that the interface is unbound from + * its driver, the reset will be skipped. * - * - This function can be called during .probe() or .disconnect() - * times. On return from .disconnect(), any pending resets will be - * cancelled. - * - * There is no no need to lock/unlock the @reset_ws as schedule_work() - * does its own. - * - * NOTE: We don't do any reference count tracking because it is not - * needed. The lifecycle of the work_struct is tied to the - * usb_interface. Before destroying the interface we cancel the - * work_struct, so the fact that work_struct is queued and or - * running means the interface (and thus, the device) exist and - * are referenced. + * - This function can be called during .probe(). It can also be called + * during .disconnect(), but doing so is pointless because the reset + * will not occur. If you really want to reset the device during + * .disconnect(), call usb_reset_device() directly -- but watch out + * for nested unbinding issues! */ void usb_queue_reset_device(struct usb_interface *iface) { - schedule_work(&iface->reset_ws); + if (schedule_work(&iface->reset_ws)) + usb_get_intf(iface); } EXPORT_SYMBOL_GPL(usb_queue_reset_device); diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index f7b7713cfb2ae3..f368d2053da534 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1551,6 +1551,7 @@ static void usb_release_interface(struct device *dev) altsetting_to_usb_interface_cache(intf->altsetting); kref_put(&intfc->ref, usb_release_interface_cache); + usb_put_dev(interface_to_usbdev(intf)); kfree(intf); } @@ -1626,24 +1627,6 @@ static struct usb_interface_assoc_descriptor *find_iad(struct usb_device *dev, /* * Internal function to queue a device reset - * - * This is initialized into the workstruct in 'struct - * usb_device->reset_ws' that is launched by - * message.c:usb_set_configuration() when initializing each 'struct - * usb_interface'. - * - * It is safe to get the USB device without reference counts because - * the life cycle of @iface is bound to the life cycle of @udev. Then, - * this function will be ran only if @iface is alive (and before - * freeing it any scheduled instances of it will have been cancelled). - * - * We need to set a flag (usb_dev->reset_running) because when we call - * the reset, the interfaces might be unbound. The current interface - * cannot try to remove the queued work as it would cause a deadlock - * (you cannot remove your work from within your executing - * workqueue). This flag lets it know, so that - * usb_cancel_queued_reset() doesn't try to do it. - * * See usb_queue_reset_device() for more details */ static void __usb_queue_reset_device(struct work_struct *ws) @@ -1655,11 +1638,10 @@ static void __usb_queue_reset_device(struct work_struct *ws) rc = usb_lock_device_for_reset(udev, iface); if (rc >= 0) { - iface->reset_running = 1; usb_reset_device(udev); - iface->reset_running = 0; usb_unlock_device(udev); } + usb_put_intf(iface); /* Undo _get_ in usb_queue_reset_device() */ } @@ -1854,6 +1836,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration) dev_set_name(&intf->dev, "%d-%s:%d.%d", dev->bus->busnum, dev->devpath, configuration, alt->desc.bInterfaceNumber); + usb_get_dev(dev); } kfree(new_interfaces); diff --git a/include/linux/usb.h b/include/linux/usb.h index f89c24a03bd9e8..058a7698d7e369 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -127,10 +127,6 @@ enum usb_interface_condition { * to the sysfs representation for that device. * @pm_usage_cnt: PM usage counter for this interface * @reset_ws: Used for scheduling resets from atomic context. - * @reset_running: set to 1 if the interface is currently running a - * queued reset so that usb_cancel_queued_reset() doesn't try to - * remove from the workqueue when running inside the worker - * thread. See __usb_queue_reset_device(). * @resetting_device: USB core reset the device, so use alt setting 0 as * current; needs bandwidth alloc after reset. * @@ -181,7 +177,6 @@ struct usb_interface { unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */ unsigned needs_altsetting0:1; /* switch to altsetting 0 is pending */ unsigned needs_binding:1; /* needs delayed unbind/rebind */ - unsigned reset_running:1; unsigned resetting_device:1; /* true: bandwidth alloc after reset */ struct device dev; /* interface specific device info */ From e4af42b63db9f258a76c515def75e4463c138d4b Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 29 Jan 2015 15:05:04 -0500 Subject: [PATCH 124/788] USB: add flag for HCDs that can't receive wakeup requests (isp1760-hcd) commit 074f9dd55f9cab1b82690ed7e44bcf38b9616ce0 upstream. Currently the USB stack assumes that all host controller drivers are capable of receiving wakeup requests from downstream devices. However, this isn't true for the isp1760-hcd driver, which means that it isn't safe to do a runtime suspend of any device attached to a root-hub port if the device requires wakeup. This patch adds a "cant_recv_wakeups" flag to the usb_hcd structure and sets the flag in isp1760-hcd. The core is modified to prevent a direct child of the root hub from being put into runtime suspend with wakeup enabled if the flag is set. Signed-off-by: Alan Stern Tested-by: Nicolas Pitre Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 12 ++++++++++++ drivers/usb/host/isp1760-hcd.c | 3 +++ include/linux/usb/hcd.h | 2 ++ 3 files changed, 17 insertions(+) diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index c76ec9758ce301..818369afff63f3 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1780,6 +1780,18 @@ static int autosuspend_check(struct usb_device *udev) dev_dbg(&udev->dev, "remote wakeup needed for autosuspend\n"); return -EOPNOTSUPP; } + + /* + * If the device is a direct child of the root hub and the HCD + * doesn't handle wakeup requests, don't allow autosuspend when + * wakeup is needed. + */ + if (w && udev->parent == udev->bus->root_hub && + bus_to_hcd(udev->bus)->cant_recv_wakeups) { + dev_dbg(&udev->dev, "HCD doesn't handle wakeup requests\n"); + return -EOPNOTSUPP; + } + udev->do_remote_wakeup = w; return 0; } diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 395649f357aa3b..c83ac8921cb3a3 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -2247,6 +2247,9 @@ struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len, hcd->rsrc_start = res_start; hcd->rsrc_len = res_len; + /* This driver doesn't support wakeup requests */ + hcd->cant_recv_wakeups = 1; + ret = usb_add_hcd(hcd, irq, irqflags); if (ret) goto err_unmap; diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 8968f616e414d6..68b1e836dff17b 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -146,6 +146,8 @@ struct usb_hcd { unsigned amd_resume_bug:1; /* AMD remote wakeup quirk */ unsigned can_do_streams:1; /* HC supports streams */ unsigned tpl_support:1; /* OTG & EH TPL support */ + unsigned cant_recv_wakeups:1; + /* wakeup requests from downstream aren't received */ unsigned int irq; /* irq allocated */ void __iomem *regs; /* device memory/io */ From 01e123b4fb2e31f3842cef4dd2343f69edcf8d09 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 28 Jan 2015 11:14:55 +0100 Subject: [PATCH 125/788] cdc-acm: add sanity checks commit 7e860a6e7aa62b337a61110430cd633db5b0d2dd upstream. Check the special CDC headers for a plausible minimum length. Another big operating systems ignores such garbage. Signed-off-by: Oliver Neukum Reviewed-by: Adam Lee Tested-by: Adam Lee Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 546a17e8ad5bef..a417b738824f13 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1091,6 +1091,7 @@ static int acm_probe(struct usb_interface *intf, unsigned long quirks; int num_rx_buf; int i; + unsigned int elength = 0; int combined_interfaces = 0; struct device *tty_dev; int rv = -ENOMEM; @@ -1136,9 +1137,12 @@ static int acm_probe(struct usb_interface *intf, dev_err(&intf->dev, "skipping garbage\n"); goto next_desc; } + elength = buffer[0]; switch (buffer[2]) { case USB_CDC_UNION_TYPE: /* we've found it */ + if (elength < sizeof(struct usb_cdc_union_desc)) + goto next_desc; if (union_header) { dev_err(&intf->dev, "More than one " "union descriptor, skipping ...\n"); @@ -1147,29 +1151,36 @@ static int acm_probe(struct usb_interface *intf, union_header = (struct usb_cdc_union_desc *)buffer; break; case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/ + if (elength < sizeof(struct usb_cdc_country_functional_desc)) + goto next_desc; cfd = (struct usb_cdc_country_functional_desc *)buffer; break; case USB_CDC_HEADER_TYPE: /* maybe check version */ break; /* for now we ignore it */ case USB_CDC_ACM_TYPE: + if (elength < 4) + goto next_desc; ac_management_function = buffer[3]; break; case USB_CDC_CALL_MANAGEMENT_TYPE: + if (elength < 5) + goto next_desc; call_management_function = buffer[3]; call_interface_num = buffer[4]; break; default: - /* there are LOTS more CDC descriptors that + /* + * there are LOTS more CDC descriptors that * could legitimately be found here. */ dev_dbg(&intf->dev, "Ignoring descriptor: " - "type %02x, length %d\n", - buffer[2], buffer[0]); + "type %02x, length %ud\n", + buffer[2], elength); break; } next_desc: - buflen -= buffer[0]; - buffer += buffer[0]; + buflen -= elength; + buffer += elength; } if (!union_header) { From 9d07ec9b44503dd3a8efbff5a626cb56dba86e8f Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 23 Jan 2015 17:07:21 -0500 Subject: [PATCH 126/788] vt: provide notifications on selection changes commit 19e3ae6b4f07a87822c1c9e7ed99d31860e701af upstream. The vcs device's poll/fasync support relies on the vt notifier to signal changes to the screen content. Notifier invocations were missing for changes that comes through the selection interface though. Fix that. Tested with BRLTTY 5.2. Signed-off-by: Nicolas Pitre Cc: Dave Mielke Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index f3fbbbca9bde1b..42d8f178fe6189 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -500,6 +500,7 @@ void invert_screen(struct vc_data *vc, int offset, int count, int viewed) #endif if (DO_UPDATE(vc)) do_update_region(vc, (unsigned long) p, count); + notify_update(vc); } /* used by selection: complement pointer position */ @@ -516,6 +517,7 @@ void complement_pos(struct vc_data *vc, int offset) scr_writew(old, screenpos(vc, old_offset, 1)); if (DO_UPDATE(vc)) vc->vc_sw->con_putc(vc, old, oldy, oldx); + notify_update(vc); } old_offset = offset; @@ -533,8 +535,8 @@ void complement_pos(struct vc_data *vc, int offset) oldy = (offset >> 1) / vc->vc_cols; vc->vc_sw->con_putc(vc, new, oldy, oldx); } + notify_update(vc); } - } static void insert_char(struct vc_data *vc, unsigned int nr) From 7febf090a21cec22ac5acf7b86dae677a494a083 Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Thu, 4 Dec 2014 14:10:00 +0300 Subject: [PATCH 127/788] ARM: pxa: add regulator_has_full_constraints to corgi board file commit 271e80176aae4e5b481f4bb92df9768c6075bbca upstream. Add regulator_has_full_constraints() call to corgi board file to let regulator core know that we do not have any additional regulators left. This lets it substitute unprovided regulators with dummy ones. This fixes the following warnings that can be seen on corgi if regulators are enabled: ads7846 spi1.0: unable to get regulator: -517 spi spi1.0: Driver ads7846 requests probe deferral wm8731 0-001b: Failed to get supply 'AVDD': -517 wm8731 0-001b: Failed to request supplies: -517 wm8731 0-001b: ASoC: failed to probe component -517 corgi-audio corgi-audio: ASoC: failed to instantiate card -517 Signed-off-by: Dmitry Eremin-Solenikov Acked-by: Mark Brown Signed-off-by: Robert Jarzmik Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-pxa/corgi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/mach-pxa/corgi.c b/arch/arm/mach-pxa/corgi.c index 06022b2357309c..89f790dda93e6c 100644 --- a/arch/arm/mach-pxa/corgi.c +++ b/arch/arm/mach-pxa/corgi.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -752,6 +753,8 @@ static void __init corgi_init(void) sharpsl_nand_partitions[1].size = 53 * 1024 * 1024; platform_add_devices(devices, ARRAY_SIZE(devices)); + + regulator_has_full_constraints(); } static void __init fixup_corgi(struct tag *tags, char **cmdline) From d20135064bee1fdd17493e0f772f0c5f396521b1 Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Thu, 4 Dec 2014 14:10:01 +0300 Subject: [PATCH 128/788] ARM: pxa: add regulator_has_full_constraints to poodle board file commit 9bc78f32c2e430aebf6def965b316aa95e37a20c upstream. Add regulator_has_full_constraints() call to poodle board file to let regulator core know that we do not have any additional regulators left. This lets it substitute unprovided regulators with dummy ones. This fixes the following warnings that can be seen on poodle if regulators are enabled: ads7846 spi1.0: unable to get regulator: -517 spi spi1.0: Driver ads7846 requests probe deferral wm8731 0-001b: Failed to get supply 'AVDD': -517 wm8731 0-001b: Failed to request supplies: -517 wm8731 0-001b: ASoC: failed to probe component -517 Signed-off-by: Dmitry Eremin-Solenikov Acked-by: Mark Brown Signed-off-by: Robert Jarzmik Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-pxa/poodle.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/mach-pxa/poodle.c b/arch/arm/mach-pxa/poodle.c index 29019beae591bc..195b1121c8f1b0 100644 --- a/arch/arm/mach-pxa/poodle.c +++ b/arch/arm/mach-pxa/poodle.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -455,6 +456,7 @@ static void __init poodle_init(void) pxa_set_i2c_info(NULL); i2c_register_board_info(0, ARRAY_AND_SIZE(poodle_i2c_devices)); poodle_init_spi(); + regulator_has_full_constraints(); } static void __init fixup_poodle(struct tag *tags, char **cmdline) From ad0a5caa6c6bfff683fb033419aa06932d1f8626 Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Thu, 4 Dec 2014 14:10:02 +0300 Subject: [PATCH 129/788] ARM: pxa: add regulator_has_full_constraints to spitz board file commit baad2dc49c5d970ea881d92981a1b76c94a7b7a1 upstream. Add regulator_has_full_constraints() call to spitz board file to let regulator core know that we do not have any additional regulators left. This lets it substitute unprovided regulators with dummy ones. This fixes the following warnings that can be seen on spitz if regulators are enabled: ads7846 spi2.0: unable to get regulator: -517 spi spi2.0: Driver ads7846 requests probe deferral Signed-off-by: Dmitry Eremin-Solenikov Acked-by: Mark Brown Signed-off-by: Robert Jarzmik Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-pxa/spitz.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/mach-pxa/spitz.c b/arch/arm/mach-pxa/spitz.c index 962a7f31f5969a..f4e2e2719580d2 100644 --- a/arch/arm/mach-pxa/spitz.c +++ b/arch/arm/mach-pxa/spitz.c @@ -979,6 +979,8 @@ static void __init spitz_init(void) spitz_nand_init(); spitz_i2c_init(); spitz_audio_init(); + + regulator_has_full_constraints(); } static void __init spitz_fixup(struct tag *tags, char **cmdline) From b6ac26ee02b8f651ade7d1eb8e03250fd7d32bb7 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 16 Dec 2014 19:13:50 -0800 Subject: [PATCH 130/788] ARM: brcmstb: update CPU power management sequence commit a1ad3b94a7661b643fef2efbc6fc217bd148f462 upstream. The automatic CPU power state machine for B15 CPUs does not work reliably as-is. This patch implements a manual sequence in software to replace it. This was tested successfully with over 10,000 hotplug cycles of something like this: echo 0 > /sys/devices/system/cpu/cpu1/online echo 1 > /sys/devices/system/cpu/cpu1/online whereas the existing sequence often locks up after a few hundred cycles. Fixes: 62639c2f5332 ("ARM: brcmstb: reintroduce SMP support") Acked-by: Gregory Fong Signed-off-by: Brian Norris Signed-off-by: Florian Fainelli Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-bcm/platsmp-brcmstb.c | 85 +++++++++++++++++++++-------- 1 file changed, 63 insertions(+), 22 deletions(-) diff --git a/arch/arm/mach-bcm/platsmp-brcmstb.c b/arch/arm/mach-bcm/platsmp-brcmstb.c index 31c87a284a3428..e209e6fc7cafa5 100644 --- a/arch/arm/mach-bcm/platsmp-brcmstb.c +++ b/arch/arm/mach-bcm/platsmp-brcmstb.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -94,10 +95,35 @@ static u32 pwr_ctrl_rd(u32 cpu) return readl_relaxed(base); } -static void pwr_ctrl_wr(u32 cpu, u32 val) +static void pwr_ctrl_set(unsigned int cpu, u32 val, u32 mask) { void __iomem *base = pwr_ctrl_get_base(cpu); - writel(val, base); + writel((readl(base) & mask) | val, base); +} + +static void pwr_ctrl_clr(unsigned int cpu, u32 val, u32 mask) +{ + void __iomem *base = pwr_ctrl_get_base(cpu); + writel((readl(base) & mask) & ~val, base); +} + +#define POLL_TMOUT_MS 500 +static int pwr_ctrl_wait_tmout(unsigned int cpu, u32 set, u32 mask) +{ + const unsigned long timeo = jiffies + msecs_to_jiffies(POLL_TMOUT_MS); + u32 tmp; + + do { + tmp = pwr_ctrl_rd(cpu) & mask; + if (!set == !tmp) + return 0; + } while (time_before(jiffies, timeo)); + + tmp = pwr_ctrl_rd(cpu) & mask; + if (!set == !tmp) + return 0; + + return -ETIMEDOUT; } static void cpu_rst_cfg_set(u32 cpu, int set) @@ -139,15 +165,22 @@ static void brcmstb_cpu_power_on(u32 cpu) * The secondary cores power was cut, so we must go through * power-on initialization. */ - u32 tmp; + pwr_ctrl_set(cpu, ZONE_MAN_ISO_CNTL_MASK, 0xffffff00); + pwr_ctrl_set(cpu, ZONE_MANUAL_CONTROL_MASK, -1); + pwr_ctrl_set(cpu, ZONE_RESERVED_1_MASK, -1); - /* Request zone power up */ - pwr_ctrl_wr(cpu, ZONE_PWR_UP_REQ_MASK); + pwr_ctrl_set(cpu, ZONE_MAN_MEM_PWR_MASK, -1); - /* Wait for the power up FSM to complete */ - do { - tmp = pwr_ctrl_rd(cpu); - } while (!(tmp & ZONE_PWR_ON_STATE_MASK)); + if (pwr_ctrl_wait_tmout(cpu, 1, ZONE_MEM_PWR_STATE_MASK)) + panic("ZONE_MEM_PWR_STATE_MASK set timeout"); + + pwr_ctrl_set(cpu, ZONE_MAN_CLKEN_MASK, -1); + + if (pwr_ctrl_wait_tmout(cpu, 1, ZONE_DPG_PWR_STATE_MASK)) + panic("ZONE_DPG_PWR_STATE_MASK set timeout"); + + pwr_ctrl_clr(cpu, ZONE_MAN_ISO_CNTL_MASK, -1); + pwr_ctrl_set(cpu, ZONE_MAN_RESET_CNTL_MASK, -1); } static int brcmstb_cpu_get_power_state(u32 cpu) @@ -174,25 +207,33 @@ static void brcmstb_cpu_die(u32 cpu) static int brcmstb_cpu_kill(u32 cpu) { - u32 tmp; + /* + * Ordinarily, the hardware forbids power-down of CPU0 (which is good + * because it is the boot CPU), but this is not true when using BPCM + * manual mode. Consequently, we must avoid turning off CPU0 here to + * ensure that TI2C master reset will work. + */ + if (cpu == 0) { + pr_warn("SMP: refusing to power off CPU0\n"); + return 1; + } while (per_cpu_sw_state_rd(cpu)) ; - /* Program zone reset */ - pwr_ctrl_wr(cpu, ZONE_RESET_STATE_MASK | ZONE_BLK_RST_ASSERT_MASK | - ZONE_PWR_DN_REQ_MASK); + pwr_ctrl_set(cpu, ZONE_MANUAL_CONTROL_MASK, -1); + pwr_ctrl_clr(cpu, ZONE_MAN_RESET_CNTL_MASK, -1); + pwr_ctrl_clr(cpu, ZONE_MAN_CLKEN_MASK, -1); + pwr_ctrl_set(cpu, ZONE_MAN_ISO_CNTL_MASK, -1); + pwr_ctrl_clr(cpu, ZONE_MAN_MEM_PWR_MASK, -1); - /* Verify zone reset */ - tmp = pwr_ctrl_rd(cpu); - if (!(tmp & ZONE_RESET_STATE_MASK)) - pr_err("%s: Zone reset bit for CPU %d not asserted!\n", - __func__, cpu); + if (pwr_ctrl_wait_tmout(cpu, 0, ZONE_MEM_PWR_STATE_MASK)) + panic("ZONE_MEM_PWR_STATE_MASK clear timeout"); - /* Wait for power down */ - do { - tmp = pwr_ctrl_rd(cpu); - } while (!(tmp & ZONE_PWR_OFF_STATE_MASK)); + pwr_ctrl_clr(cpu, ZONE_RESERVED_1_MASK, -1); + + if (pwr_ctrl_wait_tmout(cpu, 0, ZONE_DPG_PWR_STATE_MASK)) + panic("ZONE_DPG_PWR_STATE_MASK clear timeout"); /* Flush pipeline before resetting CPU */ mb(); From 49fa2998b5e19c9f216fa3951c318d1a07b4fd68 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 23 Jan 2015 20:59:10 +0100 Subject: [PATCH 131/788] ARM: BCM: put back ARCH_MULTI_V7 dependency for mobile commit ff34cae5b4fc7a84113d7c7e8611ba87a7c31dba upstream. A recent cleanup rearranged the Kconfig file for mach-bcm and accidentally dropped the dependency on ARCH_MULTI_V7, which makes it possible to now build the two mobile SoC platforms on an ARMv6-only kernel, resulting in a log of Kconfig warnings like warning: ARCH_BCM_MOBILE selects ARM_ERRATA_775420 which has unmet direct dependencies (CPU_V7) and which of course cannot work on any machine. This puts back the dependencies as before. Signed-off-by: Arnd Bergmann Fixes: 64e74aa788f99 ("ARM: mach-bcm: ARCH_BCM_MOBILE: remove one level of menu from Kconfig") Acked-by: Florian Fainelli Acked-by: Scott Branden Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-bcm/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig index aaeec78c3ec4d0..8b11f44bb36e5a 100644 --- a/arch/arm/mach-bcm/Kconfig +++ b/arch/arm/mach-bcm/Kconfig @@ -68,7 +68,7 @@ config ARCH_BCM_MOBILE This enables support for systems based on Broadcom mobile SoCs. config ARCH_BCM_281XX - bool "Broadcom BCM281XX SoC family" + bool "Broadcom BCM281XX SoC family" if ARCH_MULTI_V7 select ARCH_BCM_MOBILE select HAVE_SMP help @@ -77,7 +77,7 @@ config ARCH_BCM_281XX variants. config ARCH_BCM_21664 - bool "Broadcom BCM21664 SoC family" + bool "Broadcom BCM21664 SoC family" if ARCH_MULTI_V7 select ARCH_BCM_MOBILE select HAVE_SMP help From f5100c13e2a822190bd3601d034722ad63aed790 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 5 Feb 2015 13:42:43 +0100 Subject: [PATCH 132/788] ARM: vexpress: use ARM_CPU_SUSPEND if needed commit 95fcedb027a27f32bf2434f9271635c380e57fb5 upstream. The vexpress tc2 power management code calls mcpm_loopback, which is only available if ARM_CPU_SUSPEND is enabled, otherwise we get a link error: arch/arm/mach-vexpress/built-in.o: In function `tc2_pm_init': arch/arm/mach-vexpress/tc2_pm.c:389: undefined reference to `mcpm_loopback' This explicitly selects ARM_CPU_SUSPEND like other platforms that need it. Signed-off-by: Arnd Bergmann Fixes: 3592d7e002438 ("ARM: 8082/1: TC2: test the MCPM loopback during boot") Acked-by: Nicolas Pitre Acked-by: Liviu Dudau Cc: Kevin Hilman Cc: Sudeep Holla Cc: Lorenzo Pieralisi Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-vexpress/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-vexpress/Kconfig b/arch/arm/mach-vexpress/Kconfig index d6b16d9a78380e..3c2509b4b6946b 100644 --- a/arch/arm/mach-vexpress/Kconfig +++ b/arch/arm/mach-vexpress/Kconfig @@ -73,6 +73,7 @@ config ARCH_VEXPRESS_TC2_PM depends on MCPM select ARM_CCI select ARCH_VEXPRESS_SPC + select ARM_CPU_SUSPEND help Support for CPU and cluster power management on Versatile Express with a TC2 (A15x2 A7x3) big.LITTLE core tile. From 7dd78f5a17e61918c6d501269dea07606f56fa7b Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 2 Feb 2015 15:27:16 +0100 Subject: [PATCH 133/788] ARM: mvebu: build armada375-smp code conditionally commit 165235180ff61f0012ea68a299e46daec43dcaa7 upstream. mvebu_armada375_smp_wa_init is only used on armada 375 but is defined for all mvebu machines. As it calls a function that is only provided sometimes, this can result in a link error: arch/arm/mach-mvebu/built-in.o: In function `mvebu_armada375_smp_wa_init': :(.text+0x228): undefined reference to `mvebu_setup_boot_addr_wa' To solve this, we can just change the existing #ifdef around the function to also check for Armada375 SMP platforms. Signed-off-by: Arnd Bergmann Fixes: 305969fb6292 ("ARM: mvebu: use the common function for Armada 375 SMP workaround") Cc: Andrew Lunn Cc: Jason Cooper Cc: Gregory Clement Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-mvebu/system-controller.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-mvebu/system-controller.c b/arch/arm/mach-mvebu/system-controller.c index a068cb5c2ce809..c6c132acd7a61a 100644 --- a/arch/arm/mach-mvebu/system-controller.c +++ b/arch/arm/mach-mvebu/system-controller.c @@ -126,7 +126,7 @@ int mvebu_system_controller_get_soc_id(u32 *dev, u32 *rev) return -ENODEV; } -#ifdef CONFIG_SMP +#if defined(CONFIG_SMP) && defined(CONFIG_MACH_MVEBU_V7) void mvebu_armada375_smp_wa_init(void) { u32 dev, rev; From 10964ddc6240d6d24268a96066509993e5fbb6ca Mon Sep 17 00:00:00 2001 From: Jay Lan Date: Mon, 29 Sep 2014 15:36:57 -0700 Subject: [PATCH 134/788] kdb: fix incorrect counts in KDB summary command output commit 146755923262037fc4c54abc28c04b1103f3cc51 upstream. The output of KDB 'summary' command should report MemTotal, MemFree and Buffers output in kB. Current codes report in unit of pages. A define of K(x) as is defined in the code, but not used. This patch would apply the define to convert the values to kB. Please include me on Cc on replies. I do not subscribe to linux-kernel. Signed-off-by: Jay Lan Signed-off-by: Jason Wessel Signed-off-by: Greg Kroah-Hartman --- kernel/debug/kdb/kdb_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/debug/kdb/kdb_main.c b/kernel/debug/kdb/kdb_main.c index 7b40c5f07dce8d..c6d2c0b1565d67 100644 --- a/kernel/debug/kdb/kdb_main.c +++ b/kernel/debug/kdb/kdb_main.c @@ -2583,7 +2583,7 @@ static int kdb_summary(int argc, const char **argv) #define K(x) ((x) << (PAGE_SHIFT - 10)) kdb_printf("\nMemTotal: %8lu kB\nMemFree: %8lu kB\n" "Buffers: %8lu kB\n", - val.totalram, val.freeram, val.bufferram); + K(val.totalram), K(val.freeram), K(val.bufferram)); return 0; } From 8b29b33d2a13263adbca342ba957e882f2b79759 Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Fri, 7 Nov 2014 18:37:57 +0000 Subject: [PATCH 135/788] kdb: Avoid printing KERN_ levels to consoles commit f7d4ca8bbfda23b4f1eae9b6757ff64166b093d5 upstream. Currently when kdb traps printk messages then the raw log level prefix (consisting of '\001' followed by a numeral) does not get stripped off before the message is issued to the various I/O handlers supported by kdb. This causes annoying visual noise as well as causing problems grepping for ^. It is also a change of behaviour compared to normal usage of printk() usage. For example -h ends up with different output to that of kdb's "sr h". This patch addresses the problem by stripping log levels from messages before they are issued to the I/O handlers. printk() which can also act as an i/o handler in some cases is special cased; if the caller provided a log level then the prefix will be preserved when sent to printk(). The addition of non-printable characters to the output of kdb commands is a regression, albeit and extremely elderly one, introduced by commit 04d2c8c83d0e ("printk: convert the format for KERN_ to a 2 byte pattern"). Note also that this patch does *not* restore the original behaviour from v3.5. Instead it makes printk() from within a kdb command display the message without any prefix (i.e. like printk() normally does). Signed-off-by: Daniel Thompson Cc: Joe Perches Signed-off-by: Jason Wessel Signed-off-by: Greg Kroah-Hartman --- include/linux/kdb.h | 8 +++++++- kernel/debug/kdb/kdb_io.c | 22 +++++++++++++--------- kernel/printk/printk.c | 2 +- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/include/linux/kdb.h b/include/linux/kdb.h index 75ae2e2631fcea..a19bcf9e762e1d 100644 --- a/include/linux/kdb.h +++ b/include/linux/kdb.h @@ -156,8 +156,14 @@ typedef enum { KDB_REASON_SYSTEM_NMI, /* In NMI due to SYSTEM cmd; regs valid */ } kdb_reason_t; +enum kdb_msgsrc { + KDB_MSGSRC_INTERNAL, /* direct call to kdb_printf() */ + KDB_MSGSRC_PRINTK, /* trapped from printk() */ +}; + extern int kdb_trap_printk; -extern __printf(1, 0) int vkdb_printf(const char *fmt, va_list args); +extern __printf(2, 0) int vkdb_printf(enum kdb_msgsrc src, const char *fmt, + va_list args); extern __printf(1, 2) int kdb_printf(const char *, ...); typedef __printf(1, 2) int (*kdb_printf_t)(const char *, ...); diff --git a/kernel/debug/kdb/kdb_io.c b/kernel/debug/kdb/kdb_io.c index 7c70812caea5b3..a550afb99ebe50 100644 --- a/kernel/debug/kdb/kdb_io.c +++ b/kernel/debug/kdb/kdb_io.c @@ -548,7 +548,7 @@ static int kdb_search_string(char *searched, char *searchfor) return 0; } -int vkdb_printf(const char *fmt, va_list ap) +int vkdb_printf(enum kdb_msgsrc src, const char *fmt, va_list ap) { int diag; int linecount; @@ -691,19 +691,20 @@ int vkdb_printf(const char *fmt, va_list ap) * Write to all consoles. */ retlen = strlen(kdb_buffer); + cp = (char *) printk_skip_level(kdb_buffer); if (!dbg_kdb_mode && kgdb_connected) { - gdbstub_msg_write(kdb_buffer, retlen); + gdbstub_msg_write(cp, retlen - (cp - kdb_buffer)); } else { if (dbg_io_ops && !dbg_io_ops->is_console) { - len = retlen; - cp = kdb_buffer; + len = retlen - (cp - kdb_buffer); + cp2 = cp; while (len--) { - dbg_io_ops->write_char(*cp); - cp++; + dbg_io_ops->write_char(*cp2); + cp2++; } } while (c) { - c->write(c, kdb_buffer, retlen); + c->write(c, cp, retlen - (cp - kdb_buffer)); touch_nmi_watchdog(); c = c->next; } @@ -711,7 +712,10 @@ int vkdb_printf(const char *fmt, va_list ap) if (logging) { saved_loglevel = console_loglevel; console_loglevel = CONSOLE_LOGLEVEL_SILENT; - printk(KERN_INFO "%s", kdb_buffer); + if (printk_get_level(kdb_buffer) || src == KDB_MSGSRC_PRINTK) + printk("%s", kdb_buffer); + else + pr_info("%s", kdb_buffer); } if (KDB_STATE(PAGER)) { @@ -844,7 +848,7 @@ int kdb_printf(const char *fmt, ...) int r; va_start(ap, fmt); - r = vkdb_printf(fmt, ap); + r = vkdb_printf(KDB_MSGSRC_INTERNAL, fmt, ap); va_end(ap); return r; diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 02d6b6d2879699..fae29e3ffbf027 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -1811,7 +1811,7 @@ int vprintk_default(const char *fmt, va_list args) #ifdef CONFIG_KGDB_KDB if (unlikely(kdb_trap_printk)) { - r = vkdb_printf(fmt, args); + r = vkdb_printf(KDB_MSGSRC_PRINTK, fmt, args); return r; } #endif From 244e7e539435fcf6a81a54068afb37c1fbd5d1cb Mon Sep 17 00:00:00 2001 From: Jason Wessel Date: Thu, 8 Jan 2015 15:46:55 -0600 Subject: [PATCH 136/788] kdb: Fix off by one error in kdb_cpu() commit df0036d117e6c9df36324e517728e33543065f9a upstream. There was a follow on replacement patch against the prior "kgdb: Timeout if secondary CPUs ignore the roundup". See: https://lkml.org/lkml/2015/1/7/442 This patch is the delta vs the patch that was committed upstream: * Fix an off-by-one error in kdb_cpu(). * Replace NR_CPUS with CONFIG_NR_CPUS to tell checkpatch that we really want a static limit. * Removed the "KGDB: " prefix from the pr_crit() in debug_core.c (kgdb-next contains a patch which introduced pr_fmt() to this file to the tag will now be applied automatically). Cc: Daniel Thompson Signed-off-by: Jason Wessel Signed-off-by: Greg Kroah-Hartman --- kernel/debug/debug_core.c | 2 +- kernel/debug/kdb/kdb_main.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/debug/debug_core.c b/kernel/debug/debug_core.c index 07ce18ca71e0cd..ac5c0f9c7a20e8 100644 --- a/kernel/debug/debug_core.c +++ b/kernel/debug/debug_core.c @@ -604,7 +604,7 @@ static int kgdb_cpu_enter(struct kgdb_state *ks, struct pt_regs *regs, online_cpus) cpu_relax(); if (!time_left) - pr_crit("KGDB: Timed out waiting for secondary CPUs.\n"); + pr_crit("Timed out waiting for secondary CPUs.\n"); /* * At this point the primary processor is completely diff --git a/kernel/debug/kdb/kdb_main.c b/kernel/debug/kdb/kdb_main.c index c6d2c0b1565d67..60f6bb817f7019 100644 --- a/kernel/debug/kdb/kdb_main.c +++ b/kernel/debug/kdb/kdb_main.c @@ -2256,7 +2256,7 @@ static int kdb_cpu(int argc, const char **argv) /* * Validate cpunum */ - if ((cpunum > NR_CPUS) || !kgdb_info[cpunum].enter_kgdb) + if ((cpunum >= CONFIG_NR_CPUS) || !kgdb_info[cpunum].enter_kgdb) return KDB_BADCPUNUM; dbg_switch_cpu = cpunum; From 0bcbb5825c03329bcbe8a8c0b4a0aa20cec8a86a Mon Sep 17 00:00:00 2001 From: John Stultz Date: Mon, 9 Feb 2015 23:30:36 -0800 Subject: [PATCH 137/788] ntp: Fixup adjtimex freq validation on 32-bit systems commit 29183a70b0b828500816bd794b3fe192fce89f73 upstream. Additional validation of adjtimex freq values to avoid potential multiplication overflows were added in commit 5e5aeb4367b (time: adjtimex: Validate the ADJ_FREQUENCY values) Unfortunately the patch used LONG_MAX/MIN instead of LLONG_MAX/MIN, which was fine on 64-bit systems, but being much smaller on 32-bit systems caused false positives resulting in most direct frequency adjustments to fail w/ EINVAL. ntpd only does direct frequency adjustments at startup, so the issue was not as easily observed there, but other time sync applications like ptpd and chrony were more effected by the bug. See bugs: https://bugzilla.kernel.org/show_bug.cgi?id=92481 https://bugzilla.redhat.com/show_bug.cgi?id=1188074 This patch changes the checks to use LLONG_MAX for clarity, and additionally the checks are disabled on 32-bit systems since LLONG_MAX/PPM_SCALE is always larger then the 32-bit long freq value, so multiplication overflows aren't possible there. Reported-by: Josh Boyer Reported-by: George Joseph Tested-by: George Joseph Signed-off-by: John Stultz Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Sasha Levin Link: http://lkml.kernel.org/r/1423553436-29747-1-git-send-email-john.stultz@linaro.org [ Prettified the changelog and the comments a bit. ] Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- kernel/time/ntp.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c index 28bf91c60a0b41..85fb3d632bd880 100644 --- a/kernel/time/ntp.c +++ b/kernel/time/ntp.c @@ -633,10 +633,14 @@ int ntp_validate_timex(struct timex *txc) if ((txc->modes & ADJ_SETOFFSET) && (!capable(CAP_SYS_TIME))) return -EPERM; - if (txc->modes & ADJ_FREQUENCY) { - if (LONG_MIN / PPM_SCALE > txc->freq) + /* + * Check for potential multiplication overflows that can + * only happen on 64-bit systems: + */ + if ((txc->modes & ADJ_FREQUENCY) && (BITS_PER_LONG == 64)) { + if (LLONG_MIN / PPM_SCALE > txc->freq) return -EINVAL; - if (LONG_MAX / PPM_SCALE < txc->freq) + if (LLONG_MAX / PPM_SCALE < txc->freq) return -EINVAL; } From 0d5cb6e8b4b62d8efd1a470615894276341d6db9 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Sat, 10 Jan 2015 01:08:58 +0100 Subject: [PATCH 138/788] serial: fsl_lpuart: delete timer on shutdown commit 4a8588a1cf867333187d9ff071e6fbdab587d194 upstream. If the serial port gets closed while a RX transfer is in progress, the timer might fire after the serial port shutdown finished. This leads in a NULL pointer dereference: [ 7.508324] Unable to handle kernel NULL pointer dereference at virtual address 00000000 [ 7.516590] pgd = 86348000 [ 7.519445] [00000000] *pgd=86179831, *pte=00000000, *ppte=00000000 [ 7.526145] Internal error: Oops: 17 [#1] ARM [ 7.530611] Modules linked in: [ 7.533876] CPU: 0 PID: 123 Comm: systemd Not tainted 3.19.0-rc3-00004-g5b11ea7 #1778 [ 7.541827] Hardware name: Freescale Vybrid VF610 (Device Tree) [ 7.547862] task: 861c3400 ti: 86ac8000 task.ti: 86ac8000 [ 7.553392] PC is at lpuart_timer_func+0x24/0xf8 [ 7.558127] LR is at lpuart_timer_func+0x20/0xf8 [ 7.562857] pc : [<802df99c>] lr : [<802df998>] psr: 600b0113 [ 7.562857] sp : 86ac9b90 ip : 86ac9b90 fp : 86ac9bbc [ 7.574467] r10: 80817180 r9 : 80817b98 r8 : 80817998 [ 7.579803] r7 : 807acee0 r6 : 86989000 r5 : 00000100 r4 : 86997210 [ 7.586444] r3 : 86ac8000 r2 : 86ac9bc0 r1 : 86997210 r0 : 00000000 [ 7.593085] Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment user [ 7.600341] Control: 10c5387d Table: 86348059 DAC: 00000015 [ 7.606203] Process systemd (pid: 123, stack limit = 0x86ac8230) Setup the timer on UART startup which allows to delete the timer unconditionally on shutdown. This also saves the initialization on each transfer. Signed-off-by: Stefan Agner Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index e7cde3a9566d98..29932b73c004d7 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -506,9 +506,6 @@ static inline void lpuart_prepare_rx(struct lpuart_port *sport) spin_lock_irqsave(&sport->port.lock, flags); - init_timer(&sport->lpuart_timer); - sport->lpuart_timer.function = lpuart_timer_func; - sport->lpuart_timer.data = (unsigned long)sport; sport->lpuart_timer.expires = jiffies + sport->dma_rx_timeout; add_timer(&sport->lpuart_timer); @@ -1106,6 +1103,8 @@ static int lpuart_startup(struct uart_port *port) sport->lpuart_dma_use = false; } else { sport->lpuart_dma_use = true; + setup_timer(&sport->lpuart_timer, lpuart_timer_func, + (unsigned long)sport); temp = readb(port->membase + UARTCR5); writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5); } @@ -1180,6 +1179,8 @@ static void lpuart_shutdown(struct uart_port *port) devm_free_irq(port->dev, port->irq, sport); if (sport->lpuart_dma_use) { + del_timer_sync(&sport->lpuart_timer); + lpuart_dma_tx_free(port); lpuart_dma_rx_free(port); } From 09b73e863c0aee9d7df7ca54f01ac623a32ca5eb Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Sat, 10 Jan 2015 01:08:59 +0100 Subject: [PATCH 139/788] serial: fsl_lpuart: avoid new transfer while DMA is running commit 5f1437f61a0b351d25b528c159360da3d5e8c77b upstream. When the UART is in DMA receive mode (RDMAS set) and one character just arrived while another interrupt is handled (e.g. TX), the RDRF (receiver data register full flag) is set due to the water level of 1. But since the DMA will take care of this character, there is no need to handle it by calling lpuart_prepare_rx. Handling it leads to adding the RX timeout timer twice: [ 74.336698] Kernel BUG at 80053070 [verbose debug info unavailable] [ 74.342999] Internal error: Oops - BUG: 0 [#1] ARM0:00.00 khungtaskd [ 74.347817] Modules linked in: 0 S 0.0 0.0 0:00.00 writeback [ 74.350926] CPU: 0 PID: 0 Comm: swapper Not tainted 3.19.0-rc3-00001-g39d78e2 #1788 [ 74.358617] Hardware name: Freescale Vybrid VF610 (Device Tree)t [ 74.364563] task: 807a7678 ti: 8079c000 task.ti: 8079c000 kblockd [ 74.370002] PC is at add_timer+0x24/0x28.0 0.0 0:00.09 kworker/u2:1 [ 74.373960] LR is at lpuart_int+0x15c/0x3d8 [ 74.378171] pc : [<80053070>] lr : [<802e0d88>] psr: a0010193 [ 74.378171] sp : 8079de10 ip : 8079de20 fp : 8079de1c [ 74.389694] r10: 807d44c0 r9 : 8688c300 r8 : 00000013 [ 74.394943] r7 : 20010193 r6 : 00000000 r5 : 000000a0 r4 : 86997210 [ 74.401498] r3 : ffffa7da r2 : 80817868 r1 : 86997210 r0 : 86997344 [ 74.408052] Flags: NzCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment kernel [ 74.415489] Control: 10c5387d Table: 8611c059 DAC: 00000015 [ 74.421265] Process swapper (pid: 0, stack limit = 0x8079c230) ... Solve this by only execute the receiver path (lpuart_prepare_rx) if the DMA receive mode (RDMAS) is not set. Also, make sure the flag is cleared on initialization, in case it has been left set. This can be best reproduced using UART as a serial console, then running top while dd'ing data into the terminal. Signed-off-by: Stefan Agner Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 29932b73c004d7..e95c4971327bff 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -755,18 +755,18 @@ static irqreturn_t lpuart32_rxint(int irq, void *dev_id) static irqreturn_t lpuart_int(int irq, void *dev_id) { struct lpuart_port *sport = dev_id; - unsigned char sts; + unsigned char sts, crdma; sts = readb(sport->port.membase + UARTSR1); + crdma = readb(sport->port.membase + UARTCR5); - if (sts & UARTSR1_RDRF) { + if (sts & UARTSR1_RDRF && !(crdma & UARTCR5_RDMAS)) { if (sport->lpuart_dma_use) lpuart_prepare_rx(sport); else lpuart_rxint(irq, dev_id); } - if (sts & UARTSR1_TDRE && - !(readb(sport->port.membase + UARTCR5) & UARTCR5_TDMAS)) { + if (sts & UARTSR1_TDRE && !(crdma & UARTCR5_TDMAS)) { if (sport->lpuart_dma_use) lpuart_pio_tx(sport); else @@ -1106,6 +1106,7 @@ static int lpuart_startup(struct uart_port *port) setup_timer(&sport->lpuart_timer, lpuart_timer_func, (unsigned long)sport); temp = readb(port->membase + UARTCR5); + temp &= ~UARTCR5_RDMAS; writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5); } From 05e3d5b59eedcda1f5f16d959e8a09039ec6e992 Mon Sep 17 00:00:00 2001 From: Alexey Brodkin Date: Thu, 12 Feb 2015 21:10:11 +0300 Subject: [PATCH 140/788] ARC: fix page address calculation if PAGE_OFFSET != LINUX_LINK_BASE commit 06f34e1c28f3608b0ce5b310e41102d3fe7b65a1 upstream. We used to calculate page address differently in 2 cases: 1. In virt_to_page(x) we do --->8--- mem_map + (x - CONFIG_LINUX_LINK_BASE) >> PAGE_SHIFT --->8--- 2. In in pte_page(x) we do --->8--- mem_map + (pte_val(x) - PAGE_OFFSET) >> PAGE_SHIFT --->8--- That leads to problems in case PAGE_OFFSET != CONFIG_LINUX_LINK_BASE - different pages will be selected depending on where and how we calculate page address. In particular in the STAR 9000853582 when gdb attempted to read memory of another process it got improper page in get_user_pages() because this is exactly one of the places where we search for a page by pte_page(). The fix is trivial - we need to calculate page address similarly in both cases. Signed-off-by: Alexey Brodkin Signed-off-by: Vineet Gupta Signed-off-by: Greg Kroah-Hartman --- arch/arc/include/asm/pgtable.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arc/include/asm/pgtable.h b/arch/arc/include/asm/pgtable.h index 6b0b7f7ef783ce..7670f33b9ce2c7 100644 --- a/arch/arc/include/asm/pgtable.h +++ b/arch/arc/include/asm/pgtable.h @@ -259,7 +259,8 @@ static inline void pmd_set(pmd_t *pmdp, pte_t *ptep) #define pmd_clear(xp) do { pmd_val(*(xp)) = 0; } while (0) #define pte_page(x) (mem_map + \ - (unsigned long)(((pte_val(x) - PAGE_OFFSET) >> PAGE_SHIFT))) + (unsigned long)(((pte_val(x) - CONFIG_LINUX_LINK_BASE) >> \ + PAGE_SHIFT))) #define mk_pte(page, pgprot) \ ({ \ From 92d39ff2060c71b998817908eca399573cb31de3 Mon Sep 17 00:00:00 2001 From: Markos Chandras Date: Mon, 26 Jan 2015 13:04:33 +0000 Subject: [PATCH 141/788] MIPS: HTW: Prevent accidental HTW start due to nested htw_{start, stop} commit ed4cbc81addbc076b016c5b979fd1a02f0897f0a upstream. activate_mm() and switch_mm() call get_new_mmu_context() which in turn can enable the HTW before the entryhi is changed with the new ASID. Since the latter will enable the HTW in local_flush_tlb_all(), then there is a small timing window where the HTW is running with the new ASID but with an old pgd since the TLBMISS_HANDLER_SETUP_PGD hasn't assigned a new one yet. In order to prevent that, we introduce a simple htw counter to avoid starting HTW accidentally due to nested htw_{start,stop}() sequences. Moreover, since various IPI calls can enforce TLB flushing operations on a different core, such an operation may interrupt another htw_{stop,start} in progress leading inconsistent updates of the htw_seq variable. In order to avoid that, we disable the interrupts whenever we update that variable. Signed-off-by: Markos Chandras Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/9118/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/include/asm/cpu-info.h | 5 +++++ arch/mips/include/asm/mmu_context.h | 7 ++++++- arch/mips/include/asm/pgtable.h | 24 ++++++++++++++++++------ arch/mips/kernel/cpu-probe.c | 4 +++- 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/arch/mips/include/asm/cpu-info.h b/arch/mips/include/asm/cpu-info.h index a6c9ccb33c5c9a..c3f4f2d2e10884 100644 --- a/arch/mips/include/asm/cpu-info.h +++ b/arch/mips/include/asm/cpu-info.h @@ -84,6 +84,11 @@ struct cpuinfo_mips { * (shifted by _CACHE_SHIFT) */ unsigned int writecombine; + /* + * Simple counter to prevent enabling HTW in nested + * htw_start/htw_stop calls + */ + unsigned int htw_seq; } __attribute__((aligned(SMP_CACHE_BYTES))); extern struct cpuinfo_mips cpu_data[]; diff --git a/arch/mips/include/asm/mmu_context.h b/arch/mips/include/asm/mmu_context.h index 2f82568a3ee4cf..bc01579a907a6a 100644 --- a/arch/mips/include/asm/mmu_context.h +++ b/arch/mips/include/asm/mmu_context.h @@ -25,7 +25,6 @@ do { \ if (cpu_has_htw) { \ write_c0_pwbase(pgd); \ back_to_back_c0_hazard(); \ - htw_reset(); \ } \ } while (0) @@ -142,6 +141,7 @@ static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, unsigned long flags; local_irq_save(flags); + htw_stop(); /* Check if our ASID is of an older version and thus invalid */ if ((cpu_context(cpu, next) ^ asid_cache(cpu)) & ASID_VERSION_MASK) get_new_mmu_context(next, cpu); @@ -154,6 +154,7 @@ static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, */ cpumask_clear_cpu(cpu, mm_cpumask(prev)); cpumask_set_cpu(cpu, mm_cpumask(next)); + htw_start(); local_irq_restore(flags); } @@ -180,6 +181,7 @@ activate_mm(struct mm_struct *prev, struct mm_struct *next) local_irq_save(flags); + htw_stop(); /* Unconditionally get a new ASID. */ get_new_mmu_context(next, cpu); @@ -189,6 +191,7 @@ activate_mm(struct mm_struct *prev, struct mm_struct *next) /* mark mmu ownership change */ cpumask_clear_cpu(cpu, mm_cpumask(prev)); cpumask_set_cpu(cpu, mm_cpumask(next)); + htw_start(); local_irq_restore(flags); } @@ -203,6 +206,7 @@ drop_mmu_context(struct mm_struct *mm, unsigned cpu) unsigned long flags; local_irq_save(flags); + htw_stop(); if (cpumask_test_cpu(cpu, mm_cpumask(mm))) { get_new_mmu_context(mm, cpu); @@ -211,6 +215,7 @@ drop_mmu_context(struct mm_struct *mm, unsigned cpu) /* will get a new context next time */ cpu_context(cpu, mm) = 0; } + htw_start(); local_irq_restore(flags); } diff --git a/arch/mips/include/asm/pgtable.h b/arch/mips/include/asm/pgtable.h index 3aa982b50a10de..845016d1cdbdca 100644 --- a/arch/mips/include/asm/pgtable.h +++ b/arch/mips/include/asm/pgtable.h @@ -99,19 +99,31 @@ extern void paging_init(void); #define htw_stop() \ do { \ + unsigned long flags; \ + \ if (cpu_has_htw) { \ - write_c0_pwctl(read_c0_pwctl() & \ - ~(1 << MIPS_PWCTL_PWEN_SHIFT)); \ - back_to_back_c0_hazard(); \ + local_irq_save(flags); \ + if(!raw_current_cpu_data.htw_seq++) { \ + write_c0_pwctl(read_c0_pwctl() & \ + ~(1 << MIPS_PWCTL_PWEN_SHIFT)); \ + back_to_back_c0_hazard(); \ + } \ + local_irq_restore(flags); \ } \ } while(0) #define htw_start() \ do { \ + unsigned long flags; \ + \ if (cpu_has_htw) { \ - write_c0_pwctl(read_c0_pwctl() | \ - (1 << MIPS_PWCTL_PWEN_SHIFT)); \ - back_to_back_c0_hazard(); \ + local_irq_save(flags); \ + if (!--raw_current_cpu_data.htw_seq) { \ + write_c0_pwctl(read_c0_pwctl() | \ + (1 << MIPS_PWCTL_PWEN_SHIFT)); \ + back_to_back_c0_hazard(); \ + } \ + local_irq_restore(flags); \ } \ } while(0) diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c index 5342674842f582..228ae864c92ed7 100644 --- a/arch/mips/kernel/cpu-probe.c +++ b/arch/mips/kernel/cpu-probe.c @@ -424,8 +424,10 @@ static inline unsigned int decode_config3(struct cpuinfo_mips *c) if (config3 & MIPS_CONF3_MSA) c->ases |= MIPS_ASE_MSA; /* Only tested on 32-bit cores */ - if ((config3 & MIPS_CONF3_PW) && config_enabled(CONFIG_32BIT)) + if ((config3 & MIPS_CONF3_PW) && config_enabled(CONFIG_32BIT)) { + c->htw_seq = 0; c->options |= MIPS_CPU_HTW; + } return config3 & MIPS_CONF_M; } From 6ca5861548a24b5bd82f5475250310180b02d3b9 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 7 Jan 2015 13:46:16 +0100 Subject: [PATCH 142/788] udf: Remove repeated loads blocksize commit 79144954278d4bb5989f8b903adcac7a20ff2a5a upstream. Store blocksize in a local variable in udf_fill_inode() since it is used a lot of times. Signed-off-by: Jan Kara Signed-off-by: Greg Kroah-Hartman --- fs/udf/inode.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 5bc71d9a674a7e..95cb6970c3eafc 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -1288,6 +1288,7 @@ static int udf_read_inode(struct inode *inode, bool hidden_inode) struct kernel_lb_addr *iloc = &iinfo->i_location; unsigned int link_count; unsigned int indirections = 0; + int bs = inode->i_sb->s_blocksize; int ret = -EIO; reread: @@ -1374,38 +1375,35 @@ static int udf_read_inode(struct inode *inode, bool hidden_inode) if (fe->descTag.tagIdent == cpu_to_le16(TAG_IDENT_EFE)) { iinfo->i_efe = 1; iinfo->i_use = 0; - ret = udf_alloc_i_data(inode, inode->i_sb->s_blocksize - + ret = udf_alloc_i_data(inode, bs - sizeof(struct extendedFileEntry)); if (ret) goto out; memcpy(iinfo->i_ext.i_data, bh->b_data + sizeof(struct extendedFileEntry), - inode->i_sb->s_blocksize - - sizeof(struct extendedFileEntry)); + bs - sizeof(struct extendedFileEntry)); } else if (fe->descTag.tagIdent == cpu_to_le16(TAG_IDENT_FE)) { iinfo->i_efe = 0; iinfo->i_use = 0; - ret = udf_alloc_i_data(inode, inode->i_sb->s_blocksize - - sizeof(struct fileEntry)); + ret = udf_alloc_i_data(inode, bs - sizeof(struct fileEntry)); if (ret) goto out; memcpy(iinfo->i_ext.i_data, bh->b_data + sizeof(struct fileEntry), - inode->i_sb->s_blocksize - sizeof(struct fileEntry)); + bs - sizeof(struct fileEntry)); } else if (fe->descTag.tagIdent == cpu_to_le16(TAG_IDENT_USE)) { iinfo->i_efe = 0; iinfo->i_use = 1; iinfo->i_lenAlloc = le32_to_cpu( ((struct unallocSpaceEntry *)bh->b_data)-> lengthAllocDescs); - ret = udf_alloc_i_data(inode, inode->i_sb->s_blocksize - + ret = udf_alloc_i_data(inode, bs - sizeof(struct unallocSpaceEntry)); if (ret) goto out; memcpy(iinfo->i_ext.i_data, bh->b_data + sizeof(struct unallocSpaceEntry), - inode->i_sb->s_blocksize - - sizeof(struct unallocSpaceEntry)); + bs - sizeof(struct unallocSpaceEntry)); return 0; } @@ -1498,8 +1496,7 @@ static int udf_read_inode(struct inode *inode, bool hidden_inode) if (iinfo->i_lenAlloc != inode->i_size) goto out; /* File in ICB has to fit in there... */ - if (inode->i_size > inode->i_sb->s_blocksize - - udf_file_entry_alloc_offset(inode)) + if (inode->i_size > bs - udf_file_entry_alloc_offset(inode)) goto out; } From c2fe31b547af1f5e79e35f57f343572063ce633d Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 7 Jan 2015 13:49:08 +0100 Subject: [PATCH 143/788] udf: Check length of extended attributes and allocation descriptors commit 23b133bdc452aa441fcb9b82cbf6dd05cfd342d0 upstream. Check length of extended attributes and allocation descriptors when loading inodes from disk. Otherwise corrupted filesystems could confuse the code and make the kernel oops. Reported-by: Carl Henrik Lunde Signed-off-by: Jan Kara Signed-off-by: Greg Kroah-Hartman --- fs/udf/inode.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 95cb6970c3eafc..7b72b7dd8906b8 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -1487,6 +1487,15 @@ static int udf_read_inode(struct inode *inode, bool hidden_inode) } inode->i_generation = iinfo->i_unique; + /* + * Sanity check length of allocation descriptors and extended attrs to + * avoid integer overflows + */ + if (iinfo->i_lenEAttr > bs || iinfo->i_lenAlloc > bs) + goto out; + /* Now do exact checks */ + if (udf_file_entry_alloc_offset(inode) + iinfo->i_lenAlloc > bs) + goto out; /* Sanity checks for files in ICB so that we don't get confused later */ if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { /* From 66c3bfca4b105382dbd84118a5aa0a9664d8549a Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 11 Dec 2014 10:18:01 +0100 Subject: [PATCH 144/788] KVM: s390: forward hrtimer if guest ckc not pending yet commit 2d00f759427bb3ed963b60f570830e9eca7e1c69 upstream. Patch 0759d0681cae ("KVM: s390: cleanup handle_wait by reusing kvm_vcpu_block") changed the way pending guest clock comparator interrupts are detected. It was assumed that as soon as the hrtimer wakes up, the condition for the guest ckc is satisfied. This is however only true as long as adjclock() doesn't speed up the monotonic clock. Reason is that the hrtimer is based on CLOCK_MONOTONIC, the guest clock comparator detection is based on the raw TOD clock. If CLOCK_MONOTONIC runs faster than the TOD clock, the hrtimer wakes the target VCPU up too early and the target VCPU will not detect any pending interrupts, therefore going back to sleep. It will never be woken up again because the hrtimer has finished. The VCPU is stuck. As a quick fix, we have to forward the hrtimer until the guest clock comparator is really due, to guarantee properly timed wake ups. As the hrtimer callback might be triggered on another cpu, we have to make sure that the timer is really stopped and not currently executing the callback on another cpu. This can happen if the vcpu thread is scheduled onto another physical cpu, but the timer base is not migrated. So lets use hrtimer_cancel instead of try_to_cancel. A proper fix might be to introduce a RAW based hrtimer. Reported-by: Christian Borntraeger Signed-off-by: David Hildenbrand Acked-by: Cornelia Huck Signed-off-by: Christian Borntraeger Signed-off-by: Greg Kroah-Hartman --- arch/s390/kvm/interrupt.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index f00f31e66cd831..58713ad9c7eefe 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -820,7 +820,7 @@ int kvm_s390_handle_wait(struct kvm_vcpu *vcpu) __unset_cpu_idle(vcpu); vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu); - hrtimer_try_to_cancel(&vcpu->arch.ckc_timer); + hrtimer_cancel(&vcpu->arch.ckc_timer); return 0; } @@ -840,10 +840,20 @@ void kvm_s390_vcpu_wakeup(struct kvm_vcpu *vcpu) enum hrtimer_restart kvm_s390_idle_wakeup(struct hrtimer *timer) { struct kvm_vcpu *vcpu; + u64 now, sltime; vcpu = container_of(timer, struct kvm_vcpu, arch.ckc_timer); - kvm_s390_vcpu_wakeup(vcpu); + now = get_tod_clock_fast() + vcpu->arch.sie_block->epoch; + sltime = tod_to_ns(vcpu->arch.sie_block->ckc - now); + /* + * If the monotonic clock runs faster than the tod clock we might be + * woken up too early and have to go back to sleep to avoid deadlocks. + */ + if (vcpu->arch.sie_block->ckc > now && + hrtimer_forward_now(timer, ns_to_ktime(sltime))) + return HRTIMER_RESTART; + kvm_s390_vcpu_wakeup(vcpu); return HRTIMER_NORESTART; } From f79e5ec9d587278f5d23c86ac17d83c92a48e50b Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Fri, 12 Dec 2014 15:17:31 +0100 Subject: [PATCH 145/788] KVM: s390: base hrtimer on a monotonic clock commit 0ac96caf0f9381088c673a16d910b1d329670edf upstream. The hrtimer that handles the wait with enabled timer interrupts should not be disturbed by changes of the host time. This patch changes our hrtimer to be based on a monotonic clock. Signed-off-by: David Hildenbrand Acked-by: Cornelia Huck Signed-off-by: Christian Borntraeger Signed-off-by: Greg Kroah-Hartman --- arch/s390/kvm/kvm-s390.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 3e09801e310461..9af01dc966d073 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -670,7 +670,7 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu) if (rc) return rc; } - hrtimer_init(&vcpu->arch.ckc_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS); + hrtimer_init(&vcpu->arch.ckc_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); vcpu->arch.ckc_timer.function = kvm_s390_idle_wakeup; get_cpu_id(&vcpu->arch.cpu_id); vcpu->arch.cpu_id.version = 0xff; From a9496df45fc50af5652fadd52080ddc8889c498a Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 15 Jan 2015 17:56:18 +0100 Subject: [PATCH 146/788] KVM: s390: floating irqs: fix user triggerable endless loop commit 8e2207cdd087ebb031e9118d1fd0902c6533a5e5 upstream. If a vm with no VCPUs is created, the injection of a floating irq leads to an endless loop in the kernel. Let's skip the search for a destination VCPU for a floating irq if no VCPUs were created. Reviewed-by: Dominik Dingel Reviewed-by: Cornelia Huck Signed-off-by: David Hildenbrand Signed-off-by: Christian Borntraeger Signed-off-by: Greg Kroah-Hartman --- arch/s390/kvm/interrupt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index 58713ad9c7eefe..d6ff22d0b9bbfc 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -1197,6 +1197,8 @@ static int __inject_vm(struct kvm *kvm, struct kvm_s390_interrupt_info *inti) list_add_tail(&inti->list, &iter->list); } atomic_set(&fi->active, 1); + if (atomic_read(&kvm->online_vcpus) == 0) + goto unlock_fi; sigcpu = find_first_bit(fi->idle_mask, KVM_MAX_VCPUS); if (sigcpu == KVM_MAX_VCPUS) { do { From 57a1612afaa6f7400e4b73de7efe93282d0d2261 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Fri, 16 Jan 2015 12:58:09 +0100 Subject: [PATCH 147/788] KVM: s390: avoid memory leaks if __inject_vm() fails commit 428d53be5e7468769d4e7899cca06ed5f783a6e1 upstream. We have to delete the allocated interrupt info if __inject_vm() fails. Otherwise user space can keep flooding kvm with floating interrupts and provoke more and more memory leaks. Reported-by: Dominik Dingel Reviewed-by: Dominik Dingel Signed-off-by: David Hildenbrand Signed-off-by: Christian Borntraeger Signed-off-by: Greg Kroah-Hartman --- arch/s390/kvm/interrupt.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index d6ff22d0b9bbfc..f512cffbf84e41 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -1233,6 +1233,7 @@ int kvm_s390_inject_vm(struct kvm *kvm, struct kvm_s390_interrupt *s390int) { struct kvm_s390_interrupt_info *inti; + int rc; inti = kzalloc(sizeof(*inti), GFP_KERNEL); if (!inti) @@ -1280,7 +1281,10 @@ int kvm_s390_inject_vm(struct kvm *kvm, trace_kvm_s390_inject_vm(s390int->type, s390int->parm, s390int->parm64, 2); - return __inject_vm(kvm, inti); + rc = __inject_vm(kvm, inti); + if (rc) + kfree(inti); + return rc; } void kvm_s390_reinject_io_int(struct kvm *kvm, From b20a2c494ade408d39379a121e0a9ad41f2f78fb Mon Sep 17 00:00:00 2001 From: Martin Vajnar Date: Wed, 24 Dec 2014 00:27:57 +0100 Subject: [PATCH 148/788] hx4700: regulator: declare full constraints commit a52d209336f8fc7483a8c7f4a8a7d2a8e1692a6c upstream. Since the removal of CONFIG_REGULATOR_DUMMY option, the touchscreen stopped working. This patch enables the "replacement" for REGULATOR_DUMMY and allows the touchscreen to work even though there is no regulator for "vcc". Signed-off-by: Martin Vajnar Signed-off-by: Robert Jarzmik Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-pxa/hx4700.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/mach-pxa/hx4700.c b/arch/arm/mach-pxa/hx4700.c index c66ad4edc5e35e..5fb41ad6e3bcdc 100644 --- a/arch/arm/mach-pxa/hx4700.c +++ b/arch/arm/mach-pxa/hx4700.c @@ -893,6 +893,8 @@ static void __init hx4700_init(void) mdelay(10); gpio_set_value(GPIO71_HX4700_ASIC3_nRESET, 1); mdelay(10); + + regulator_has_full_constraints(); } MACHINE_START(H4700, "HP iPAQ HX4700") From 85520d339d9f48894b7fe3dbec72bcabe4903cb2 Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Mon, 23 Feb 2015 15:13:40 +0000 Subject: [PATCH 149/788] arm64: compat Fix siginfo_t -> compat_siginfo_t conversion on big endian commit 9d42d48a342aee208c1154696196497fdc556bbf upstream. The native (64-bit) sigval_t union contains sival_int (32-bit) and sival_ptr (64-bit). When a compat application invokes a syscall that takes a sigval_t value (as part of a larger structure, e.g. compat_sys_mq_notify, compat_sys_timer_create), the compat_sigval_t union is converted to the native sigval_t with sival_int overlapping with either the least or the most significant half of sival_ptr, depending on endianness. When the corresponding signal is delivered to a compat application, on big endian the current (compat_uptr_t)sival_ptr cast always returns 0 since sival_int corresponds to the top part of sival_ptr. This patch fixes copy_siginfo_to_user32() so that sival_int is copied to the compat_siginfo_t structure. Reported-by: Bamvor Jian Zhang Tested-by: Bamvor Jian Zhang Signed-off-by: Catalin Marinas Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kernel/signal32.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/arm64/kernel/signal32.c b/arch/arm64/kernel/signal32.c index 5a1ba6e80d4e20..6ae93403aae01d 100644 --- a/arch/arm64/kernel/signal32.c +++ b/arch/arm64/kernel/signal32.c @@ -154,8 +154,7 @@ int copy_siginfo_to_user32(compat_siginfo_t __user *to, const siginfo_t *from) case __SI_TIMER: err |= __put_user(from->si_tid, &to->si_tid); err |= __put_user(from->si_overrun, &to->si_overrun); - err |= __put_user((compat_uptr_t)(unsigned long)from->si_ptr, - &to->si_ptr); + err |= __put_user(from->si_int, &to->si_int); break; case __SI_POLL: err |= __put_user(from->si_band, &to->si_band); @@ -184,7 +183,7 @@ int copy_siginfo_to_user32(compat_siginfo_t __user *to, const siginfo_t *from) case __SI_MESGQ: /* But this is */ err |= __put_user(from->si_pid, &to->si_pid); err |= __put_user(from->si_uid, &to->si_uid); - err |= __put_user((compat_uptr_t)(unsigned long)from->si_ptr, &to->si_ptr); + err |= __put_user(from->si_int, &to->si_int); break; case __SI_SYS: err |= __put_user((compat_uptr_t)(unsigned long) From 83a2ef9a74681002714a2564b79a85a02c3631e8 Mon Sep 17 00:00:00 2001 From: Hans Holmberg Date: Tue, 10 Feb 2015 09:48:27 +0100 Subject: [PATCH 150/788] gpiolib: of: allow of_gpiochip_find_and_xlate to find more than one chip per node commit 9cf75e9e4ddd587ac12e88e8751c358b7b27e95f upstream. The change: 7b8792bbdffdff3abda704f89c6a45ea97afdc62 gpiolib: of: Correct error handling in of_get_named_gpiod_flags assumed that only one gpio-chip is registred per of-node. Some drivers register more than one chip per of-node, so adjust the matching function of_gpiochip_find_and_xlate to not stop looking for chips if a node-match is found and the translation fails. Fixes: 7b8792bbdffd ("gpiolib: of: Correct error handling in of_get_named_gpiod_flags") Signed-off-by: Hans Holmberg Acked-by: Alexandre Courbot Tested-by: Robert Jarzmik Tested-by: Tyler Hall Signed-off-by: Linus Walleij Signed-off-by: Greg Kroah-Hartman --- drivers/gpio/gpiolib-of.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 08261f2b3a82af..26645a847bb823 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -46,12 +46,13 @@ static int of_gpiochip_find_and_xlate(struct gpio_chip *gc, void *data) ret = gc->of_xlate(gc, &gg_data->gpiospec, gg_data->flags); if (ret < 0) { - /* We've found the gpio chip, but the translation failed. - * Return true to stop looking and return the translation - * error via out_gpio + /* We've found a gpio chip, but the translation failed. + * Store translation error in out_gpio. + * Return false to keep looking, as more than one gpio chip + * could be registered per of-node. */ gg_data->out_gpio = ERR_PTR(ret); - return true; + return false; } gg_data->out_gpio = gpiochip_get_desc(gc, ret); From 3f165340feeaccfbe2afb1bbacfd1460ebec9b75 Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Thu, 19 Feb 2015 01:52:25 +0000 Subject: [PATCH 151/788] gpio: tps65912: fix wrong container_of arguments commit 2f97c20e5f7c3582c7310f65a04465bfb0fd0e85 upstream. The gpio_chip operations receive a pointer the gpio_chip struct which is contained in the driver's private struct, yet the container_of call in those functions point to the mfd struct defined in include/linux/mfd/tps65912.h. Signed-off-by: Nicolas Saenz Julienne Signed-off-by: Linus Walleij Signed-off-by: Greg Kroah-Hartman --- drivers/gpio/gpio-tps65912.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-tps65912.c b/drivers/gpio/gpio-tps65912.c index 472fb5b8779f37..9cdbc0c9cb2da8 100644 --- a/drivers/gpio/gpio-tps65912.c +++ b/drivers/gpio/gpio-tps65912.c @@ -26,9 +26,12 @@ struct tps65912_gpio_data { struct gpio_chip gpio_chip; }; +#define to_tgd(gc) container_of(gc, struct tps65912_gpio_data, gpio_chip) + static int tps65912_gpio_get(struct gpio_chip *gc, unsigned offset) { - struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio); + struct tps65912_gpio_data *tps65912_gpio = to_tgd(gc); + struct tps65912 *tps65912 = tps65912_gpio->tps65912; int val; val = tps65912_reg_read(tps65912, TPS65912_GPIO1 + offset); @@ -42,7 +45,8 @@ static int tps65912_gpio_get(struct gpio_chip *gc, unsigned offset) static void tps65912_gpio_set(struct gpio_chip *gc, unsigned offset, int value) { - struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio); + struct tps65912_gpio_data *tps65912_gpio = to_tgd(gc); + struct tps65912 *tps65912 = tps65912_gpio->tps65912; if (value) tps65912_set_bits(tps65912, TPS65912_GPIO1 + offset, @@ -55,7 +59,8 @@ static void tps65912_gpio_set(struct gpio_chip *gc, unsigned offset, static int tps65912_gpio_output(struct gpio_chip *gc, unsigned offset, int value) { - struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio); + struct tps65912_gpio_data *tps65912_gpio = to_tgd(gc); + struct tps65912 *tps65912 = tps65912_gpio->tps65912; /* Set the initial value */ tps65912_gpio_set(gc, offset, value); @@ -66,7 +71,8 @@ static int tps65912_gpio_output(struct gpio_chip *gc, unsigned offset, static int tps65912_gpio_input(struct gpio_chip *gc, unsigned offset) { - struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio); + struct tps65912_gpio_data *tps65912_gpio = to_tgd(gc); + struct tps65912 *tps65912 = tps65912_gpio->tps65912; return tps65912_clear_bits(tps65912, TPS65912_GPIO1 + offset, GPIO_CFG_MASK); From 251276cfcd716dfd02f0feed8e55759f200f28f9 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 23 Feb 2015 22:34:17 +1100 Subject: [PATCH 152/788] xfs: Fix quota type in quota structures when reusing quota file commit dfcc70a8c868fe03276fa59864149708fb41930b upstream. For filesystems without separate project quota inode field in the superblock we just reuse project quota file for group quotas (and vice versa) if project quota file is allocated and we need group quota file. When we reuse the file, quota structures on disk suddenly have wrong type stored in d_flags though. Nobody really cares about this (although structure type reported to userspace was wrong as well) except that after commit 14bf61ffe6ac (quota: Switch ->get_dqblk() and ->set_dqblk() to use bytes as space units) assertion in xfs_qm_scall_getquota() started to trigger on xfs/106 test (apparently I was testing without XFS_DEBUG so I didn't notice when submitting the above commit). Fix the problem by properly resetting ddq->d_flags when running quotacheck for a quota file. Reported-by: Al Viro Signed-off-by: Jan Kara Reviewed-by: Dave Chinner Signed-off-by: Dave Chinner Signed-off-by: Greg Kroah-Hartman --- fs/xfs/xfs_qm.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index 79fb19dd9c833d..79a62bb95fdeae 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -842,6 +842,11 @@ xfs_qm_reset_dqcounts( */ xfs_dqcheck(mp, ddq, id+j, type, XFS_QMOPT_DQREPAIR, "xfs_quotacheck"); + /* + * Reset type in case we are reusing group quota file for + * project quotas or vice versa + */ + ddq->d_flags = type; ddq->d_bcount = 0; ddq->d_icount = 0; ddq->d_rtbcount = 0; From 5d58e15cc18f95bc06769795aa5237f2b525150e Mon Sep 17 00:00:00 2001 From: James Hogan Date: Tue, 24 Feb 2015 12:25:25 +0000 Subject: [PATCH 153/788] metag: Fix KSTK_EIP() and KSTK_ESP() macros commit c2996cb29bfb73927a79dc96e598a718e843f01a upstream. The KSTK_EIP() and KSTK_ESP() macros should return the user program counter (PC) and stack pointer (A0StP) of the given task. These are used to determine which VMA corresponds to the user stack in /proc//maps, and for the user PC & A0StP in /proc//stat. However for Meta the PC & A0StP from the task's kernel context are used, resulting in broken output. For example in following /proc//maps output, the 3afff000-3b021000 VMA should be described as the stack: # cat /proc/self/maps ... 100b0000-100b1000 rwxp 00000000 00:00 0 [heap] 3afff000-3b021000 rwxp 00000000 00:00 0 And in the following /proc//stat output, the PC is in kernel code (1074234964 = 0x40078654) and the A0StP is in the kernel heap (1335981392 = 0x4fa17550): # cat /proc/self/stat 51 (cat) R ... 1335981392 1074234964 ... Fix the definitions of KSTK_EIP() and KSTK_ESP() to use task_pt_regs(tsk)->ctx rather than (tsk)->thread.kernel_context. This gets the registers from the user context stored after the thread info at the base of the kernel stack, which is from the last entry into the kernel from userland, regardless of where in the kernel the task may have been interrupted, which results in the following more correct /proc//maps output: # cat /proc/self/maps ... 0800b000-08070000 r-xp 00000000 00:02 207 /lib/libuClibc-0.9.34-git.so ... 100b0000-100b1000 rwxp 00000000 00:00 0 [heap] 3afff000-3b021000 rwxp 00000000 00:00 0 [stack] And /proc//stat now correctly reports the PC in libuClibc (134320308 = 0x80190b4) and the A0StP in the [stack] region (989864576 = 0x3b002280): # cat /proc/self/stat 51 (cat) R ... 989864576 134320308 ... Reported-by: Alexey Brodkin Reported-by: Vineet Gupta Signed-off-by: James Hogan Cc: linux-metag@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- arch/metag/include/asm/processor.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/metag/include/asm/processor.h b/arch/metag/include/asm/processor.h index 881071c0794221..13272fd5a5baec 100644 --- a/arch/metag/include/asm/processor.h +++ b/arch/metag/include/asm/processor.h @@ -149,8 +149,8 @@ extern void exit_thread(void); unsigned long get_wchan(struct task_struct *p); -#define KSTK_EIP(tsk) ((tsk)->thread.kernel_context->CurrPC) -#define KSTK_ESP(tsk) ((tsk)->thread.kernel_context->AX[0].U0) +#define KSTK_EIP(tsk) (task_pt_regs(tsk)->ctx.CurrPC) +#define KSTK_ESP(tsk) (task_pt_regs(tsk)->ctx.AX[0].U0) #define user_stack_pointer(regs) ((regs)->ctx.AX[0].U0) From 1c09fe950fbf5f1cbb13385f2c91cdcb2f5f4fa0 Mon Sep 17 00:00:00 2001 From: Matthias Brugger Date: Thu, 19 Feb 2015 11:41:33 +0100 Subject: [PATCH 154/788] clocksource: mtk: Fix race conditions in probe code commit d4a19eb3b15a4ba98f627182f48d5bc0cffae670 upstream. We have two race conditions in the probe code which could lead to a null pointer dereference in the interrupt handler. The interrupt handler accesses the clockevent device, which may not yet be registered. First race condition happens when the interrupt handler gets registered before the interrupts get disabled. The second race condition happens when the interrupts get enabled, but the clockevent device is not yet registered. Fix that by disabling the interrupts before we register the interrupt and enable the interrupts after the clockevent device got registered. Reported-by: Gongbae Park Signed-off-by: Matthias Brugger Signed-off-by: Daniel Lezcano Signed-off-by: Greg Kroah-Hartman --- drivers/clocksource/mtk_timer.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/clocksource/mtk_timer.c b/drivers/clocksource/mtk_timer.c index 32a3d25795d3a2..68ab42356d0e7a 100644 --- a/drivers/clocksource/mtk_timer.c +++ b/drivers/clocksource/mtk_timer.c @@ -224,6 +224,8 @@ static void __init mtk_timer_init(struct device_node *node) } rate = clk_get_rate(clk); + mtk_timer_global_reset(evt); + if (request_irq(evt->dev.irq, mtk_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL, "mtk_timer", evt)) { pr_warn("failed to setup irq %d\n", evt->dev.irq); @@ -232,8 +234,6 @@ static void __init mtk_timer_init(struct device_node *node) evt->ticks_per_jiffy = DIV_ROUND_UP(rate, HZ); - mtk_timer_global_reset(evt); - /* Configure clock source */ mtk_timer_setup(evt, GPT_CLK_SRC, TIMER_CTRL_OP_FREERUN); clocksource_mmio_init(evt->gpt_base + TIMER_CNT_REG(GPT_CLK_SRC), @@ -241,10 +241,11 @@ static void __init mtk_timer_init(struct device_node *node) /* Configure clock event */ mtk_timer_setup(evt, GPT_CLK_EVT, TIMER_CTRL_OP_REPEAT); - mtk_timer_enable_irq(evt, GPT_CLK_EVT); - clockevents_config_and_register(&evt->dev, rate, 0x3, 0xffffffff); + + mtk_timer_enable_irq(evt, GPT_CLK_EVT); + return; err_clk_disable: From 1a15e64c3afce5542925a68ac6c571124801555c Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 24 Feb 2015 13:20:59 +0200 Subject: [PATCH 155/788] perf tools: Fix probing for PERF_FLAG_FD_CLOEXEC flag commit 48536c9195ae8c2a00fd8f400bac72ab613feaab upstream. Commit f6edb53c4993ffe92ce521fb449d1c146cea6ec2 converted the probe to a CPU wide event first (pid == -1). For kernels that do not support the PERF_FLAG_FD_CLOEXEC flag the probe fails with EINVAL. Since this errno is not handled pid is not reset to 0 and the subsequent use of pid = -1 as an argument brings in an additional failure path if perf_event_paranoid > 0: $ perf record -- sleep 1 perf_event_open(..., 0) failed unexpectedly with error 13 (Permission denied) [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.007 MB /tmp/perf.data (11 samples) ] Also, ensure the fd of the confirmation check is closed and comment why pid = -1 is used. Needs to go to 3.18 stable tree as well. Signed-off-by: Adrian Hunter Based-on-patch-by: David Ahern Acked-by: David Ahern Cc: David Ahern Link: http://lkml.kernel.org/r/54EC610C.8000403@intel.com Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Greg Kroah-Hartman --- tools/perf/util/cloexec.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/cloexec.c b/tools/perf/util/cloexec.c index 47b78b3f032573..6da965bdbc2caf 100644 --- a/tools/perf/util/cloexec.c +++ b/tools/perf/util/cloexec.c @@ -25,6 +25,10 @@ static int perf_flag_probe(void) if (cpu < 0) cpu = 0; + /* + * Using -1 for the pid is a workaround to avoid gratuitous jump label + * changes. + */ while (1) { /* check cloexec flag */ fd = sys_perf_event_open(&attr, pid, cpu, -1, @@ -47,16 +51,24 @@ static int perf_flag_probe(void) err, strerror_r(err, sbuf, sizeof(sbuf))); /* not supported, confirm error related to PERF_FLAG_FD_CLOEXEC */ - fd = sys_perf_event_open(&attr, pid, cpu, -1, 0); + while (1) { + fd = sys_perf_event_open(&attr, pid, cpu, -1, 0); + if (fd < 0 && pid == -1 && errno == EACCES) { + pid = 0; + continue; + } + break; + } err = errno; + if (fd >= 0) + close(fd); + if (WARN_ONCE(fd < 0 && err != EBUSY, "perf_event_open(..., 0) failed unexpectedly with error %d (%s)\n", err, strerror_r(err, sbuf, sizeof(sbuf)))) return -1; - close(fd); - return 0; } From 97ec321278ff96e9cbf88f99a911463118ea8be2 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Wed, 18 Feb 2015 11:35:14 +1100 Subject: [PATCH 156/788] md/raid5: Fix livelock when array is both resyncing and degraded. commit 26ac107378c4742978216be1005b7291b799c7b2 upstream. Commit a7854487cd7128a30a7f4f5259de9f67d5efb95f: md: When RAID5 is dirty, force reconstruct-write instead of read-modify-write. Causes an RCW cycle to be forced even when the array is degraded. A degraded array cannot support RCW as that requires reading all data blocks, and one may be missing. Forcing an RCW when it is not possible causes a live-lock and the code spins, repeatedly deciding to do something that cannot succeed. So change the condition to only force RCW on non-degraded arrays. Reported-by: Manibalan P Bisected-by: Jes Sorensen Tested-by: Jes Sorensen Signed-off-by: NeilBrown Fixes: a7854487cd7128a30a7f4f5259de9f67d5efb95f Signed-off-by: Greg Kroah-Hartman --- drivers/md/raid5.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index b98765f6f77fd9..8577cc7db47ef6 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -3102,7 +3102,8 @@ static void handle_stripe_dirtying(struct r5conf *conf, * generate correct data from the parity. */ if (conf->max_degraded == 2 || - (recovery_cp < MaxSector && sh->sector >= recovery_cp)) { + (recovery_cp < MaxSector && sh->sector >= recovery_cp && + s->failed == 0)) { /* Calculate the real rcw later - for now make it * look like rcw is cheaper */ From 63a7d0a9160421a23baac407847e484e8488d12a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hodek?= Date: Mon, 23 Feb 2015 11:00:38 +1100 Subject: [PATCH 157/788] md/raid1: fix read balance when a drive is write-mostly. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit d1901ef099c38afd11add4cfb3312c02ef21ec4a upstream. When a drive is marked write-mostly it should only be the target of reads if there is no other option. This behaviour was broken by commit 9dedf60313fa4dddfd5b9b226a0ef12a512bf9dc md/raid1: read balance chooses idlest disk for SSD which causes a write-mostly device to be *preferred* is some cases. Restore correct behaviour by checking and setting best_dist_disk and best_pending_disk rather than best_disk. We only need to test one of these as they are both changed from -1 or >=0 at the same time. As we leave min_pending and best_dist unchanged, any non-write-mostly device will appear better than the write-mostly device. Reported-by: Tomáš Hodek Reported-by: Dark Penguin Signed-off-by: NeilBrown Link: http://marc.info/?l=linux-raid&m=135982797322422 Fixes: 9dedf60313fa4dddfd5b9b226a0ef12a512bf9dc Signed-off-by: Greg Kroah-Hartman --- drivers/md/raid1.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 40b35be34f8d84..2f2f38f4d83c82 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -560,7 +560,7 @@ static int read_balance(struct r1conf *conf, struct r1bio *r1_bio, int *max_sect if (test_bit(WriteMostly, &rdev->flags)) { /* Don't balance among write-mostly, just * use the first as a last resort */ - if (best_disk < 0) { + if (best_dist_disk < 0) { if (is_badblock(rdev, this_sector, sectors, &first_bad, &bad_sectors)) { if (first_bad < this_sector) @@ -569,7 +569,8 @@ static int read_balance(struct r1conf *conf, struct r1bio *r1_bio, int *max_sect best_good_sectors = first_bad - this_sector; } else best_good_sectors = sectors; - best_disk = disk; + best_dist_disk = disk; + best_pending_disk = disk; } continue; } From e7afafa82e799a946cc1b88de2af9c23dc267687 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Thu, 5 Feb 2015 12:39:36 +0100 Subject: [PATCH 158/788] sb_edac: Fix detection on SNB machines commit 11249e73992981e31fd50e7231da24fad68e3320 upstream. d0585cd815fa ("sb_edac: Claim a different PCI device") changed the probing of sb_edac to look for PCI device 0x3ca0: 3f:0e.0 System peripheral: Intel Corporation Xeon E5/Core i7 Processor Home Agent (rev 07) 00: 86 80 a0 3c 00 00 00 00 07 00 80 08 00 00 80 00 ... but we're matching for 0x3ca8, i.e. PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TA in sbridge_probe() therefore the probing fails. Changing it to probe for 0x3ca0 (PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_HA0), .i.e., the 14.0 device, fixes the issue and driver loads successfully again: [ 2449.013120] EDAC DEBUG: sbridge_init: [ 2449.017029] EDAC sbridge: Seeking for: PCI ID 8086:3ca0 [ 2449.022368] EDAC DEBUG: sbridge_get_onedevice: Detected 8086:3ca0 [ 2449.028498] EDAC sbridge: Seeking for: PCI ID 8086:3ca0 [ 2449.033768] EDAC sbridge: Seeking for: PCI ID 8086:3ca8 [ 2449.039028] EDAC DEBUG: sbridge_get_onedevice: Detected 8086:3ca8 [ 2449.045155] EDAC sbridge: Seeking for: PCI ID 8086:3ca8 ... Add a debug printk while at it to be able to catch the failure in the future and dump driver version on successful load. Fixes: d0585cd815fa ("sb_edac: Claim a different PCI device") Acked-by: Aristeu Rozanski Cc: Tony Luck Acked-by: Andy Lutomirski Acked-by: Mauro Carvalho Chehab Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/edac/sb_edac.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c index 63aa6730e89ea5..1acf57ba4c86bd 100644 --- a/drivers/edac/sb_edac.c +++ b/drivers/edac/sb_edac.c @@ -2447,7 +2447,7 @@ static int sbridge_probe(struct pci_dev *pdev, const struct pci_device_id *id) rc = sbridge_get_all_devices(&num_mc, pci_dev_descr_ibridge_table); type = IVY_BRIDGE; break; - case PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TA: + case PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_HA0: rc = sbridge_get_all_devices(&num_mc, pci_dev_descr_sbridge_table); type = SANDY_BRIDGE; break; @@ -2460,8 +2460,11 @@ static int sbridge_probe(struct pci_dev *pdev, const struct pci_device_id *id) type = BROADWELL; break; } - if (unlikely(rc < 0)) + if (unlikely(rc < 0)) { + edac_dbg(0, "couldn't get all devices for 0x%x\n", pdev->device); goto fail0; + } + mc = 0; list_for_each_entry(sbridge_dev, &sbridge_edac_list, list) { @@ -2474,7 +2477,7 @@ static int sbridge_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto fail1; } - sbridge_printk(KERN_INFO, "Driver loaded.\n"); + sbridge_printk(KERN_INFO, "%s\n", SBRIDGE_REVISION); mutex_unlock(&sbridge_edac_lock); return 0; From 6d209d9ada74279a3fb64b0ab873fe8e4a11f853 Mon Sep 17 00:00:00 2001 From: Daniel J Blueman Date: Tue, 17 Feb 2015 11:34:38 +0800 Subject: [PATCH 159/788] EDAC, amd64_edac: Prevent OOPS with >16 memory controllers commit 0c510cc83bdbaac8406f4f7caef34f4da0ba35ea upstream. When DRAM errors occur on memory controllers after EDAC_MAX_MCS (16), the kernel fatally dereferences unallocated structures, see splat below; this occurs on at least NumaConnect systems. Fix by checking if a memory controller info structure was found. BUG: unable to handle kernel NULL pointer dereference at 0000000000000320 IP: [] decode_bus_error+0x2f/0x2b0 PGD 2f8b5a3067 PUD 2f8b5a2067 PMD 0 Oops: 0000 [#2] SMP Modules linked in: CPU: 224 PID: 11930 Comm: stream_c.exe.gn Tainted: G D 3.19.0 #1 Hardware name: Supermicro H8QGL/H8QGL, BIOS 3.5b 01/28/2015 task: ffff8807dbfb8c00 ti: ffff8807dd16c000 task.ti: ffff8807dd16c000 RIP: 0010:[] [] decode_bus_error+0x2f/0x2b0 RSP: 0000:ffff8907dfc03c48 EFLAGS: 00010297 RAX: 0000000000000001 RBX: 9c67400010080a13 RCX: 0000000000001dc6 RDX: 000000001dc61dc6 RSI: ffff8907dfc03df0 RDI: 000000000000001c RBP: ffff8907dfc03ce8 R08: 0000000000000000 R09: 0000000000000022 R10: ffff891fffa30380 R11: 00000000001cfc90 R12: 0000000000000008 R13: 0000000000000000 R14: 000000000000001c R15: 00009c6740001000 FS: 00007fa97ee18700(0000) GS:ffff8907dfc00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000320 CR3: 0000003f889b8000 CR4: 00000000000407e0 Stack: 0000000000000000 ffff8907dfc03df0 0000000000000008 9c67400010080a13 000000000000001c 00009c6740001000 ffff8907dfc03c88 ffffffff810e4f9a ffff8907dfc03ce8 ffffffff81b375b9 0000000000000000 0000000000000010 Call Trace: ? vprintk_default ? printk amd_decode_mce notifier_call_chain atomic_notifier_call_chain mce_log machine_check_poll mce_timer_fn ? mce_cpu_restart call_timer_fn.isra.29 run_timer_softirq __do_softirq irq_exit smp_apic_timer_interrupt apic_timer_interrupt ? down_read_trylock __do_page_fault ? __schedule do_page_fault page_fault Signed-off-by: Daniel J Blueman Link: http://lkml.kernel.org/r/1424144078-24589-1-git-send-email-daniel@numascale.com [ Boris: massage commit message ] Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/edac/amd64_edac.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 17638d7cf5c279..5907c1718f8c74 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -2174,14 +2174,20 @@ static void __log_bus_error(struct mem_ctl_info *mci, struct err_info *err, static inline void decode_bus_error(int node_id, struct mce *m) { - struct mem_ctl_info *mci = mcis[node_id]; - struct amd64_pvt *pvt = mci->pvt_info; + struct mem_ctl_info *mci; + struct amd64_pvt *pvt; u8 ecc_type = (m->status >> 45) & 0x3; u8 xec = XEC(m->status, 0x1f); u16 ec = EC(m->status); u64 sys_addr; struct err_info err; + mci = edac_mc_find(node_id); + if (!mci) + return; + + pvt = mci->pvt_info; + /* Bail out early if this was an 'observed' error */ if (PP(ec) == NBSL_PP_OBS) return; From c84a6855897baaf2b57dbdadec91ea6841d154fd Mon Sep 17 00:00:00 2001 From: Chen Jie Date: Tue, 10 Feb 2015 12:49:48 -0800 Subject: [PATCH 160/788] jffs2: fix handling of corrupted summary length commit 164c24063a3eadee11b46575c5482b2f1417be49 upstream. sm->offset maybe wrong but magic maybe right, the offset do not have CRC. Badness at c00c7580 [verbose debug info unavailable] NIP: c00c7580 LR: c00c718c CTR: 00000014 REGS: df07bb40 TRAP: 0700 Not tainted (2.6.34.13-WR4.3.0.0_standard) MSR: 00029000 CR: 22084f84 XER: 00000000 TASK = df84d6e0[908] 'mount' THREAD: df07a000 GPR00: 00000001 df07bbf0 df84d6e0 00000000 00000001 00000000 df07bb58 00000041 GPR08: 00000041 c0638860 00000000 00000010 22084f88 100636c8 df814ff8 00000000 GPR16: df84d6e0 dfa558cc c05adb90 00000048 c0452d30 00000000 000240d0 000040d0 GPR24: 00000014 c05ae734 c05be2e0 00000000 00000001 00000000 00000000 c05ae730 NIP [c00c7580] __alloc_pages_nodemask+0x4d0/0x638 LR [c00c718c] __alloc_pages_nodemask+0xdc/0x638 Call Trace: [df07bbf0] [c00c718c] __alloc_pages_nodemask+0xdc/0x638 (unreliable) [df07bc90] [c00c7708] __get_free_pages+0x20/0x48 [df07bca0] [c00f4a40] __kmalloc+0x15c/0x1ec [df07bcd0] [c01fc880] jffs2_scan_medium+0xa58/0x14d0 [df07bd70] [c01ff38c] jffs2_do_mount_fs+0x1f4/0x6b4 [df07bdb0] [c020144c] jffs2_do_fill_super+0xa8/0x260 [df07bdd0] [c020230c] jffs2_fill_super+0x104/0x184 [df07be00] [c0335814] get_sb_mtd_aux+0x9c/0xec [df07be20] [c033596c] get_sb_mtd+0x84/0x1e8 [df07be60] [c0201ed0] jffs2_get_sb+0x1c/0x2c [df07be70] [c0103898] vfs_kern_mount+0x78/0x1e8 [df07bea0] [c0103a58] do_kern_mount+0x40/0x100 [df07bec0] [c011fe90] do_mount+0x240/0x890 [df07bf10] [c0120570] sys_mount+0x90/0xd8 [df07bf40] [c00110d8] ret_from_syscall+0x0/0x4 === Exception: c01 at 0xff61a34 LR = 0x100135f0 Instruction dump: 38800005 38600000 48010f41 4bfffe1c 4bfc2d15 4bfffe8c 72e90200 4082fc28 3d20c064 39298860 8809000d 68000001 <0f000000> 2f800000 419efc0c 38000001 mount: mounting /dev/mtdblock3 on /common failed: Input/output error Signed-off-by: Chen Jie Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse Signed-off-by: Greg Kroah-Hartman --- fs/jffs2/scan.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c index 7654e87b042869..9ad5ba4b299be2 100644 --- a/fs/jffs2/scan.c +++ b/fs/jffs2/scan.c @@ -510,6 +510,10 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo sumlen = c->sector_size - je32_to_cpu(sm->offset); sumptr = buf + buf_size - sumlen; + /* sm->offset maybe wrong but MAGIC maybe right */ + if (sumlen > c->sector_size) + goto full_scan; + /* Now, make sure the summary itself is available */ if (sumlen > buf_size) { /* Need to kmalloc for this. */ @@ -544,6 +548,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo } } +full_scan: buf_ofs = jeb->offset; if (!buf_size) { From c0accdab94c4aedd71c08a8d3f8fc65e5cac4405 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 9 Jan 2015 15:10:01 +0100 Subject: [PATCH 161/788] samsung-laptop: Add use_native_backlight quirk, and enable it on some models commit 4690555e13c48fef07f2762f6b0cd6b181e326d0 upstream. Since kernel 3.14 the backlight control has been broken on various Samsung Atom based netbooks. This has been bisected and this problem happens since commit b35684b8fa94 ("drm/i915: do full backlight setup at enable time") This has been reported and discussed in detail here: http://lists.freedesktop.org/archives/intel-gfx/2014-July/049395.html Unfortunately no-one has been able to fix this. This only affects Samsung Atom netbooks, and the Linux kernel and the BIOS of those laptops have never worked well together. All affected laptops already have a quirk to avoid using the standard acpi-video interface and instead use the samsung specific SABI interface which samsung-laptop uses. It seems that recent fixes to the i915 driver have also broken backlight control through the SABI interface. The intel_backlight driver OTOH works fine, and also allows for finer grained backlight control. So add a new use_native_backlight quirk, and replace the broken_acpi_video quirk with this quirk for affected models. This new quirk disables acpi-video as before and also stops samsung-laptop from registering the SABI based samsung_laptop backlight interface, leaving only the working intel_backlight interface. This commit enables this new quirk for 3 models which are known to be affected, chances are that it needs to be used on other models too. BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=1094948 # N145P BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=1115713 # N250P Reported-by: Bertrik Sikken # N150P Cc: stable@vger.kernel.org # 3.16 Signed-off-by: Hans de Goede Signed-off-by: Darren Hart Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/samsung-laptop.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index ff765d8e1a09f6..ce364a41842a2a 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -353,6 +353,7 @@ struct samsung_quirks { bool broken_acpi_video; bool four_kbd_backlight_levels; bool enable_kbd_backlight; + bool use_native_backlight; }; static struct samsung_quirks samsung_unknown = {}; @@ -361,6 +362,10 @@ static struct samsung_quirks samsung_broken_acpi_video = { .broken_acpi_video = true, }; +static struct samsung_quirks samsung_use_native_backlight = { + .use_native_backlight = true, +}; + static struct samsung_quirks samsung_np740u3e = { .four_kbd_backlight_levels = true, .enable_kbd_backlight = true, @@ -1507,7 +1512,7 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "N150P"), DMI_MATCH(DMI_BOARD_NAME, "N150P"), }, - .driver_data = &samsung_broken_acpi_video, + .driver_data = &samsung_use_native_backlight, }, { .callback = samsung_dmi_matched, @@ -1517,7 +1522,7 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"), DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"), }, - .driver_data = &samsung_broken_acpi_video, + .driver_data = &samsung_use_native_backlight, }, { .callback = samsung_dmi_matched, @@ -1557,7 +1562,7 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "N250P"), DMI_MATCH(DMI_BOARD_NAME, "N250P"), }, - .driver_data = &samsung_broken_acpi_video, + .driver_data = &samsung_use_native_backlight, }, { .callback = samsung_dmi_matched, @@ -1616,6 +1621,15 @@ static int __init samsung_init(void) pr_info("Disabling ACPI video driver\n"); acpi_video_unregister(); } + + if (samsung->quirks->use_native_backlight) { + pr_info("Using native backlight driver\n"); + /* Tell acpi-video to not handle the backlight */ + acpi_video_dmi_promote_vendor(); + acpi_video_unregister(); + /* And also do not handle it ourselves */ + samsung->handle_backlight = false; + } #endif ret = samsung_platform_init(samsung); From 1c9f7cbfc80535d0bc558518fd7613755b0cb34f Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Tue, 17 Feb 2015 19:37:15 +0300 Subject: [PATCH 162/788] libceph: fix double __remove_osd() problem commit 7eb71e0351fbb1b242ae70abb7bb17107fe2f792 upstream. It turns out it's possible to get __remove_osd() called twice on the same OSD. That doesn't sit well with rb_erase() - depending on the shape of the tree we can get a NULL dereference, a soft lockup or a random crash at some point in the future as we end up touching freed memory. One scenario that I was able to reproduce is as follows: con_fault_finish() osd_reset() ceph_osdc_handle_map() kick_requests() reset_changed_osds() __reset_osd() __remove_osd() __kick_osd_requests() __reset_osd() __remove_osd() <-- !!! A case can be made that osd refcounting is imperfect and reworking it would be a proper resolution, but for now Sage and I decided to fix this by adding a safe guard around __remove_osd(). Fixes: http://tracker.ceph.com/issues/8087 Cc: Sage Weil Signed-off-by: Ilya Dryomov Reviewed-by: Sage Weil Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- net/ceph/osd_client.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 53299c7b0ca4a5..f693a2f8ac86f3 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -1048,14 +1048,24 @@ static void put_osd(struct ceph_osd *osd) */ static void __remove_osd(struct ceph_osd_client *osdc, struct ceph_osd *osd) { - dout("__remove_osd %p\n", osd); + dout("%s %p osd%d\n", __func__, osd, osd->o_osd); WARN_ON(!list_empty(&osd->o_requests)); WARN_ON(!list_empty(&osd->o_linger_requests)); - rb_erase(&osd->o_node, &osdc->osds); list_del_init(&osd->o_osd_lru); - ceph_con_close(&osd->o_con); - put_osd(osd); + rb_erase(&osd->o_node, &osdc->osds); + RB_CLEAR_NODE(&osd->o_node); +} + +static void remove_osd(struct ceph_osd_client *osdc, struct ceph_osd *osd) +{ + dout("%s %p osd%d\n", __func__, osd, osd->o_osd); + + if (!RB_EMPTY_NODE(&osd->o_node)) { + ceph_con_close(&osd->o_con); + __remove_osd(osdc, osd); + put_osd(osd); + } } static void remove_all_osds(struct ceph_osd_client *osdc) @@ -1065,7 +1075,7 @@ static void remove_all_osds(struct ceph_osd_client *osdc) while (!RB_EMPTY_ROOT(&osdc->osds)) { struct ceph_osd *osd = rb_entry(rb_first(&osdc->osds), struct ceph_osd, o_node); - __remove_osd(osdc, osd); + remove_osd(osdc, osd); } mutex_unlock(&osdc->request_mutex); } @@ -1106,7 +1116,7 @@ static void remove_old_osds(struct ceph_osd_client *osdc) list_for_each_entry_safe(osd, nosd, &osdc->osd_lru, o_osd_lru) { if (time_before(jiffies, osd->lru_ttl)) break; - __remove_osd(osdc, osd); + remove_osd(osdc, osd); } mutex_unlock(&osdc->request_mutex); } @@ -1121,8 +1131,7 @@ static int __reset_osd(struct ceph_osd_client *osdc, struct ceph_osd *osd) dout("__reset_osd %p osd%d\n", osd, osd->o_osd); if (list_empty(&osd->o_requests) && list_empty(&osd->o_linger_requests)) { - __remove_osd(osdc, osd); - + remove_osd(osdc, osd); return -ENODEV; } @@ -1926,6 +1935,7 @@ static void reset_changed_osds(struct ceph_osd_client *osdc) { struct rb_node *p, *n; + dout("%s %p\n", __func__, osdc); for (p = rb_first(&osdc->osds); p; p = n) { struct ceph_osd *osd = rb_entry(p, struct ceph_osd, o_node); From 1c034fa385b2c0fdea8f2fb9fb112c98c8b91adb Mon Sep 17 00:00:00 2001 From: David Sterba Date: Fri, 19 Dec 2014 18:38:47 +0100 Subject: [PATCH 163/788] btrfs: set proper message level for skinny metadata commit 5efa0490cc94aee06cd8d282683e22a8ce0a0026 upstream. This has been confusing people for too long, the message is really just informative. Signed-off-by: David Sterba Signed-off-by: Chris Mason Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/disk-io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 8c63419a7f70de..418d0b65cec14c 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -2498,7 +2498,7 @@ int open_ctree(struct super_block *sb, features |= BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO; if (features & BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA) - printk(KERN_ERR "BTRFS: has skinny extents\n"); + printk(KERN_INFO "BTRFS: has skinny extents\n"); /* * flag our filesystem as having big metadata blocks if From 46da9e765e0d01b6abd5eec8044c50006deca134 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Fri, 2 Jan 2015 18:45:16 +0100 Subject: [PATCH 164/788] btrfs: fix leak of path in btrfs_find_item commit 381cf6587f8a8a8e981bc0c1aaaa8859b51dc756 upstream. If btrfs_find_item is called with NULL path it allocates one locally but does not free it. Affected paths are inserting an orphan item for a file and for a subvol root. Move the path allocation to the callers. Fixes: 3f870c289900 ("btrfs: expand btrfs_find_item() to include find_orphan_item functionality") Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/ctree.c | 17 ++++------------- fs/btrfs/disk-io.c | 9 ++++++++- fs/btrfs/tree-log.c | 11 ++++++++++- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 14a72ed14ef7b1..f54511dd287ebc 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -2609,32 +2609,23 @@ static int key_search(struct extent_buffer *b, struct btrfs_key *key, return 0; } -int btrfs_find_item(struct btrfs_root *fs_root, struct btrfs_path *found_path, +int btrfs_find_item(struct btrfs_root *fs_root, struct btrfs_path *path, u64 iobjectid, u64 ioff, u8 key_type, struct btrfs_key *found_key) { int ret; struct btrfs_key key; struct extent_buffer *eb; - struct btrfs_path *path; + + ASSERT(path); key.type = key_type; key.objectid = iobjectid; key.offset = ioff; - if (found_path == NULL) { - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - } else - path = found_path; - ret = btrfs_search_slot(NULL, fs_root, &key, path, 0, 0); - if ((ret < 0) || (found_key == NULL)) { - if (path != found_path) - btrfs_free_path(path); + if ((ret < 0) || (found_key == NULL)) return ret; - } eb = path->nodes[0]; if (ret && path->slots[0] >= btrfs_header_nritems(eb)) { diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 418d0b65cec14c..6f46c9b1f50c75 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -1630,6 +1630,7 @@ struct btrfs_root *btrfs_get_fs_root(struct btrfs_fs_info *fs_info, bool check_ref) { struct btrfs_root *root; + struct btrfs_path *path; int ret; if (location->objectid == BTRFS_ROOT_TREE_OBJECTID) @@ -1669,8 +1670,14 @@ struct btrfs_root *btrfs_get_fs_root(struct btrfs_fs_info *fs_info, if (ret) goto fail; - ret = btrfs_find_item(fs_info->tree_root, NULL, BTRFS_ORPHAN_OBJECTID, + path = btrfs_alloc_path(); + if (!path) { + ret = -ENOMEM; + goto fail; + } + ret = btrfs_find_item(fs_info->tree_root, path, BTRFS_ORPHAN_OBJECTID, location->objectid, BTRFS_ORPHAN_ITEM_KEY, NULL); + btrfs_free_path(path); if (ret < 0) goto fail; if (ret == 0) diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 1a9585d4380a33..906934e2bd8ca8 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -1257,10 +1257,19 @@ static int insert_orphan_item(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 offset) { int ret; - ret = btrfs_find_item(root, NULL, BTRFS_ORPHAN_OBJECTID, + struct btrfs_path *path; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + ret = btrfs_find_item(root, path, BTRFS_ORPHAN_OBJECTID, offset, BTRFS_ORPHAN_ITEM_KEY, NULL); if (ret > 0) ret = btrfs_insert_orphan_item(trans, root, offset); + + btrfs_free_path(path); + return ret; } From c5966bf78165ff493aefe87f4e81b267858764c5 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Fri, 13 Feb 2015 12:30:56 +0000 Subject: [PATCH 165/788] Btrfs: fix fsync data loss after adding hard link to inode commit 1a4bcf470c886b955adf36486f4c86f2441d85cb upstream. We have a scenario where after the fsync log replay we can lose file data that had been previously fsync'ed if we added an hard link for our inode and after that we sync'ed the fsync log (for example by fsync'ing some other file or directory). This is because when adding an hard link we updated the inode item in the log tree with an i_size value of 0. At that point the new inode item was in memory only and a subsequent fsync log replay would not make us lose the file data. However if after adding the hard link we sync the log tree to disk, by fsync'ing some other file or directory for example, we ended up losing the file data after log replay, because the inode item in the persisted log tree had an an i_size of zero. This is easy to reproduce, and the following excerpt from my test for xfstests shows this: _scratch_mkfs >> $seqres.full 2>&1 _init_flakey _mount_flakey # Create one file with data and fsync it. # This made the btrfs fsync log persist the data and the inode metadata with # a correct inode->i_size (4096 bytes). $XFS_IO_PROG -f -c "pwrite -S 0xaa -b 4K 0 4K" -c "fsync" \ $SCRATCH_MNT/foo | _filter_xfs_io # Now add one hard link to our file. This made the btrfs code update the fsync # log, in memory only, with an inode metadata having a size of 0. ln $SCRATCH_MNT/foo $SCRATCH_MNT/foo_link # Now force persistence of the fsync log to disk, for example, by fsyncing some # other file. touch $SCRATCH_MNT/bar $XFS_IO_PROG -c "fsync" $SCRATCH_MNT/bar # Before a power loss or crash, we could read the 4Kb of data from our file as # expected. echo "File content before:" od -t x1 $SCRATCH_MNT/foo # Simulate a crash/power loss. _load_flakey_table $FLAKEY_DROP_WRITES _unmount_flakey _load_flakey_table $FLAKEY_ALLOW_WRITES _mount_flakey # After the fsync log replay, because the fsync log had a value of 0 for our # inode's i_size, we couldn't read anymore the 4Kb of data that we previously # wrote and fsync'ed. The size of the file became 0 after the fsync log replay. echo "File content after:" od -t x1 $SCRATCH_MNT/foo Another alternative test, that doesn't need to fsync an inode in the same transaction it was created, is: _scratch_mkfs >> $seqres.full 2>&1 _init_flakey _mount_flakey # Create our test file with some data. $XFS_IO_PROG -f -c "pwrite -S 0xaa -b 8K 0 8K" \ $SCRATCH_MNT/foo | _filter_xfs_io # Make sure the file is durably persisted. sync # Append some data to our file, to increase its size. $XFS_IO_PROG -f -c "pwrite -S 0xcc -b 4K 8K 4K" \ $SCRATCH_MNT/foo | _filter_xfs_io # Fsync the file, so from this point on if a crash/power failure happens, our # new data is guaranteed to be there next time the fs is mounted. $XFS_IO_PROG -c "fsync" $SCRATCH_MNT/foo # Add one hard link to our file. This made btrfs write into the in memory fsync # log a special inode with generation 0 and an i_size of 0 too. Note that this # didn't update the inode in the fsync log on disk. ln $SCRATCH_MNT/foo $SCRATCH_MNT/foo_link # Now make sure the in memory fsync log is durably persisted. # Creating and fsync'ing another file will do it. touch $SCRATCH_MNT/bar $XFS_IO_PROG -c "fsync" $SCRATCH_MNT/bar # As expected, before the crash/power failure, we should be able to read the # 12Kb of file data. echo "File content before:" od -t x1 $SCRATCH_MNT/foo # Simulate a crash/power loss. _load_flakey_table $FLAKEY_DROP_WRITES _unmount_flakey _load_flakey_table $FLAKEY_ALLOW_WRITES _mount_flakey # After mounting the fs again, the fsync log was replayed. # The btrfs fsync log replay code didn't update the i_size of the persisted # inode because the inode item in the log had a special generation with a # value of 0 (and it couldn't know the correct i_size, since that inode item # had a 0 i_size too). This made the last 4Kb of file data inaccessible and # effectively lost. echo "File content after:" od -t x1 $SCRATCH_MNT/foo This isn't a new issue/regression. This problem has been around since the log tree code was added in 2008: Btrfs: Add a write ahead tree log to optimize synchronous operations (commit e02119d5a7b4396c5a872582fddc8bd6d305a70a) Test cases for xfstests follow soon. Signed-off-by: Filipe Manana Signed-off-by: Chris Mason Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/tree-log.c | 82 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 73 insertions(+), 9 deletions(-) diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 906934e2bd8ca8..f78e9dc5d5747d 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -488,8 +488,20 @@ static noinline int overwrite_item(struct btrfs_trans_handle *trans, src_item = (struct btrfs_inode_item *)src_ptr; dst_item = (struct btrfs_inode_item *)dst_ptr; - if (btrfs_inode_generation(eb, src_item) == 0) + if (btrfs_inode_generation(eb, src_item) == 0) { + struct extent_buffer *dst_eb = path->nodes[0]; + + if (S_ISREG(btrfs_inode_mode(eb, src_item)) && + S_ISREG(btrfs_inode_mode(dst_eb, dst_item))) { + struct btrfs_map_token token; + u64 ino_size = btrfs_inode_size(eb, src_item); + + btrfs_init_map_token(&token); + btrfs_set_token_inode_size(dst_eb, dst_item, + ino_size, &token); + } goto no_copy; + } if (overwrite_root && S_ISDIR(btrfs_inode_mode(eb, src_item)) && @@ -3228,7 +3240,8 @@ static int drop_objectid_items(struct btrfs_trans_handle *trans, static void fill_inode_item(struct btrfs_trans_handle *trans, struct extent_buffer *leaf, struct btrfs_inode_item *item, - struct inode *inode, int log_inode_only) + struct inode *inode, int log_inode_only, + u64 logged_isize) { struct btrfs_map_token token; @@ -3241,7 +3254,7 @@ static void fill_inode_item(struct btrfs_trans_handle *trans, * to say 'update this inode with these values' */ btrfs_set_token_inode_generation(leaf, item, 0, &token); - btrfs_set_token_inode_size(leaf, item, 0, &token); + btrfs_set_token_inode_size(leaf, item, logged_isize, &token); } else { btrfs_set_token_inode_generation(leaf, item, BTRFS_I(inode)->generation, @@ -3293,7 +3306,7 @@ static int log_inode_item(struct btrfs_trans_handle *trans, return ret; inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0], struct btrfs_inode_item); - fill_inode_item(trans, path->nodes[0], inode_item, inode, 0); + fill_inode_item(trans, path->nodes[0], inode_item, inode, 0, 0); btrfs_release_path(path); return 0; } @@ -3302,7 +3315,8 @@ static noinline int copy_items(struct btrfs_trans_handle *trans, struct inode *inode, struct btrfs_path *dst_path, struct btrfs_path *src_path, u64 *last_extent, - int start_slot, int nr, int inode_only) + int start_slot, int nr, int inode_only, + u64 logged_isize) { unsigned long src_offset; unsigned long dst_offset; @@ -3359,7 +3373,8 @@ static noinline int copy_items(struct btrfs_trans_handle *trans, dst_path->slots[0], struct btrfs_inode_item); fill_inode_item(trans, dst_path->nodes[0], inode_item, - inode, inode_only == LOG_INODE_EXISTS); + inode, inode_only == LOG_INODE_EXISTS, + logged_isize); } else { copy_extent_buffer(dst_path->nodes[0], src, dst_offset, src_offset, ins_sizes[i]); @@ -3911,6 +3926,33 @@ static int btrfs_log_changed_extents(struct btrfs_trans_handle *trans, return ret; } +static int logged_inode_size(struct btrfs_root *log, struct inode *inode, + struct btrfs_path *path, u64 *size_ret) +{ + struct btrfs_key key; + int ret; + + key.objectid = btrfs_ino(inode); + key.type = BTRFS_INODE_ITEM_KEY; + key.offset = 0; + + ret = btrfs_search_slot(NULL, log, &key, path, 0, 0); + if (ret < 0) { + return ret; + } else if (ret > 0) { + *size_ret = i_size_read(inode); + } else { + struct btrfs_inode_item *item; + + item = btrfs_item_ptr(path->nodes[0], path->slots[0], + struct btrfs_inode_item); + *size_ret = btrfs_inode_size(path->nodes[0], item); + } + + btrfs_release_path(path); + return 0; +} + /* log a single inode in the tree log. * At least one parent directory for this inode must exist in the tree * or be logged already. @@ -3948,6 +3990,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans, bool fast_search = false; u64 ino = btrfs_ino(inode); struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree; + u64 logged_isize = 0; path = btrfs_alloc_path(); if (!path) @@ -4001,6 +4044,25 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans, max_key_type = BTRFS_XATTR_ITEM_KEY; ret = drop_objectid_items(trans, log, path, ino, max_key_type); } else { + if (inode_only == LOG_INODE_EXISTS) { + /* + * Make sure the new inode item we write to the log has + * the same isize as the current one (if it exists). + * This is necessary to prevent data loss after log + * replay, and also to prevent doing a wrong expanding + * truncate - for e.g. create file, write 4K into offset + * 0, fsync, write 4K into offset 4096, add hard link, + * fsync some other file (to sync log), power fail - if + * we use the inode's current i_size, after log replay + * we get a 8Kb file, with the last 4Kb extent as a hole + * (zeroes), as if an expanding truncate happened, + * instead of getting a file of 4Kb only. + */ + err = logged_inode_size(log, inode, path, + &logged_isize); + if (err) + goto out_unlock; + } if (test_and_clear_bit(BTRFS_INODE_NEEDS_FULL_SYNC, &BTRFS_I(inode)->runtime_flags)) { clear_bit(BTRFS_INODE_COPY_EVERYTHING, @@ -4056,7 +4118,8 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans, } ret = copy_items(trans, inode, dst_path, path, &last_extent, - ins_start_slot, ins_nr, inode_only); + ins_start_slot, ins_nr, inode_only, + logged_isize); if (ret < 0) { err = ret; goto out_unlock; @@ -4080,7 +4143,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans, if (ins_nr) { ret = copy_items(trans, inode, dst_path, path, &last_extent, ins_start_slot, - ins_nr, inode_only); + ins_nr, inode_only, logged_isize); if (ret < 0) { err = ret; goto out_unlock; @@ -4101,7 +4164,8 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans, } if (ins_nr) { ret = copy_items(trans, inode, dst_path, path, &last_extent, - ins_start_slot, ins_nr, inode_only); + ins_start_slot, ins_nr, inode_only, + logged_isize); if (ret < 0) { err = ret; goto out_unlock; From 84cf433355b87753cdc67d7405ad88e9ff1f28de Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Mon, 16 Feb 2015 17:16:45 -0200 Subject: [PATCH 166/788] blk-throttle: check stats_cpu before reading it from sysfs commit 045c47ca306acf30c740c285a77a4b4bda6be7c5 upstream. When reading blkio.throttle.io_serviced in a recently created blkio cgroup, it's possible to race against the creation of a throttle policy, which delays the allocation of stats_cpu. Like other functions in the throttle code, just checking for a NULL stats_cpu prevents the following oops caused by that race. [ 1117.285199] Unable to handle kernel paging request for data at address 0x7fb4d0020 [ 1117.285252] Faulting instruction address: 0xc0000000003efa2c [ 1137.733921] Oops: Kernel access of bad area, sig: 11 [#1] [ 1137.733945] SMP NR_CPUS=2048 NUMA PowerNV [ 1137.734025] Modules linked in: bridge stp llc kvm_hv kvm binfmt_misc autofs4 [ 1137.734102] CPU: 3 PID: 5302 Comm: blkcgroup Not tainted 3.19.0 #5 [ 1137.734132] task: c000000f1d188b00 ti: c000000f1d210000 task.ti: c000000f1d210000 [ 1137.734167] NIP: c0000000003efa2c LR: c0000000003ef9f0 CTR: c0000000003ef980 [ 1137.734202] REGS: c000000f1d213500 TRAP: 0300 Not tainted (3.19.0) [ 1137.734230] MSR: 9000000000009032 CR: 42008884 XER: 20000000 [ 1137.734325] CFAR: 0000000000008458 DAR: 00000007fb4d0020 DSISR: 40000000 SOFTE: 0 GPR00: c0000000003ed3a0 c000000f1d213780 c000000000c59538 0000000000000000 GPR04: 0000000000000800 0000000000000000 0000000000000000 0000000000000000 GPR08: ffffffffffffffff 00000007fb4d0020 00000007fb4d0000 c000000000780808 GPR12: 0000000022000888 c00000000fdc0d80 0000000000000000 0000000000000000 GPR16: 0000000000000000 0000000000000000 0000000000000000 0000000000000000 GPR20: 000001003e120200 c000000f1d5b0cc0 0000000000000200 0000000000000000 GPR24: 0000000000000001 c000000000c269e0 0000000000000020 c000000f1d5b0c80 GPR28: c000000000ca3a08 c000000000ca3dec c000000f1c667e00 c000000f1d213850 [ 1137.734886] NIP [c0000000003efa2c] .tg_prfill_cpu_rwstat+0xac/0x180 [ 1137.734915] LR [c0000000003ef9f0] .tg_prfill_cpu_rwstat+0x70/0x180 [ 1137.734943] Call Trace: [ 1137.734952] [c000000f1d213780] [d000000005560520] 0xd000000005560520 (unreliable) [ 1137.734996] [c000000f1d2138a0] [c0000000003ed3a0] .blkcg_print_blkgs+0xe0/0x1a0 [ 1137.735039] [c000000f1d213960] [c0000000003efb50] .tg_print_cpu_rwstat+0x50/0x70 [ 1137.735082] [c000000f1d2139e0] [c000000000104b48] .cgroup_seqfile_show+0x58/0x150 [ 1137.735125] [c000000f1d213a70] [c0000000002749dc] .kernfs_seq_show+0x3c/0x50 [ 1137.735161] [c000000f1d213ae0] [c000000000218630] .seq_read+0xe0/0x510 [ 1137.735197] [c000000f1d213bd0] [c000000000275b04] .kernfs_fop_read+0x164/0x200 [ 1137.735240] [c000000f1d213c80] [c0000000001eb8e0] .__vfs_read+0x30/0x80 [ 1137.735276] [c000000f1d213cf0] [c0000000001eb9c4] .vfs_read+0x94/0x1b0 [ 1137.735312] [c000000f1d213d90] [c0000000001ebb38] .SyS_read+0x58/0x100 [ 1137.735349] [c000000f1d213e30] [c000000000009218] syscall_exit+0x0/0x98 [ 1137.735383] Instruction dump: [ 1137.735405] 7c6307b4 7f891800 409d00b8 60000000 60420000 3d420004 392a63b0 786a1f24 [ 1137.735471] 7d49502a e93e01c8 7d495214 7d2ad214 <7cead02a> e9090008 e9490010 e9290018 And here is one code that allows to easily reproduce this, although this has first been found by running docker. void run(pid_t pid) { int n; int status; int fd; char *buffer; buffer = memalign(BUFFER_ALIGN, BUFFER_SIZE); n = snprintf(buffer, BUFFER_SIZE, "%d\n", pid); fd = open(CGPATH "/test/tasks", O_WRONLY); write(fd, buffer, n); close(fd); if (fork() > 0) { fd = open("/dev/sda", O_RDONLY | O_DIRECT); read(fd, buffer, 512); close(fd); wait(&status); } else { fd = open(CGPATH "/test/blkio.throttle.io_serviced", O_RDONLY); n = read(fd, buffer, BUFFER_SIZE); close(fd); } free(buffer); exit(0); } void test(void) { int status; mkdir(CGPATH "/test", 0666); if (fork() > 0) wait(&status); else run(getpid()); rmdir(CGPATH "/test"); } int main(int argc, char **argv) { int i; for (i = 0; i < NR_TESTS; i++) test(); return 0; } Reported-by: Ricardo Marin Matinata Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/blk-throttle.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 9273d0969ebd63..5b9c6d5c3636ad 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -1292,6 +1292,9 @@ static u64 tg_prfill_cpu_rwstat(struct seq_file *sf, struct blkg_rwstat rwstat = { }, tmp; int i, cpu; + if (tg->stats_cpu == NULL) + return 0; + for_each_possible_cpu(cpu) { struct tg_stats_cpu *sc = per_cpu_ptr(tg->stats_cpu, cpu); From c4f934bc8afc2d9ba633c9fc0f0ce593338ecae9 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Tue, 13 Jan 2015 15:25:00 +0000 Subject: [PATCH 167/788] x86/efi: Avoid triple faults during EFI mixed mode calls commit 96738c69a7fcdbf0d7c9df0c8a27660011e82a7b upstream. Andy pointed out that if an NMI or MCE is received while we're in the middle of an EFI mixed mode call a triple fault will occur. This can happen, for example, when issuing an EFI mixed mode call while running perf. The reason for the triple fault is that we execute the mixed mode call in 32-bit mode with paging disabled but with 64-bit kernel IDT handlers installed throughout the call. At Andy's suggestion, stop playing the games we currently do at runtime, such as disabling paging and installing a 32-bit GDT for __KERNEL_CS. We can simply switch to the __KERNEL32_CS descriptor before invoking firmware services, and run in compatibility mode. This way, if an NMI/MCE does occur the kernel IDT handler will execute correctly, since it'll jump to __KERNEL_CS automatically. However, this change is only possible post-ExitBootServices(). Before then the firmware "owns" the machine and expects for its 32-bit IDT handlers to be left intact to service interrupts, etc. So, we now need to distinguish between early boot and runtime invocations of EFI services. During early boot, we need to restore the GDT that the firmware expects to be present. We can only jump to the __KERNEL32_CS code segment for mixed mode calls after ExitBootServices() has been invoked. A liberal sprinkling of comments in the thunking code should make the differences in early and late environments more apparent. Reported-by: Andy Lutomirski Tested-by: Borislav Petkov Signed-off-by: Matt Fleming Signed-off-by: Greg Kroah-Hartman --- arch/x86/boot/compressed/Makefile | 1 + arch/x86/boot/compressed/efi_stub_64.S | 25 --- arch/x86/boot/compressed/efi_thunk_64.S | 196 ++++++++++++++++++++++++ arch/x86/platform/efi/efi_stub_64.S | 161 ------------------- arch/x86/platform/efi/efi_thunk_64.S | 121 +++++++++++++-- 5 files changed, 301 insertions(+), 203 deletions(-) create mode 100644 arch/x86/boot/compressed/efi_thunk_64.S diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile index ad754b4411f7e4..8bd44e8ee6e2a5 100644 --- a/arch/x86/boot/compressed/Makefile +++ b/arch/x86/boot/compressed/Makefile @@ -49,6 +49,7 @@ $(obj)/eboot.o: KBUILD_CFLAGS += -fshort-wchar -mno-red-zone vmlinux-objs-$(CONFIG_EFI_STUB) += $(obj)/eboot.o $(obj)/efi_stub_$(BITS).o \ $(objtree)/drivers/firmware/efi/libstub/lib.a +vmlinux-objs-$(CONFIG_EFI_MIXED) += $(obj)/efi_thunk_$(BITS).o $(obj)/vmlinux: $(vmlinux-objs-y) FORCE $(call if_changed,ld) diff --git a/arch/x86/boot/compressed/efi_stub_64.S b/arch/x86/boot/compressed/efi_stub_64.S index 7ff3632806b18e..99494dff2113e5 100644 --- a/arch/x86/boot/compressed/efi_stub_64.S +++ b/arch/x86/boot/compressed/efi_stub_64.S @@ -3,28 +3,3 @@ #include #include "../../platform/efi/efi_stub_64.S" - -#ifdef CONFIG_EFI_MIXED - .code64 - .text -ENTRY(efi64_thunk) - push %rbp - push %rbx - - subq $16, %rsp - leaq efi_exit32(%rip), %rax - movl %eax, 8(%rsp) - leaq efi_gdt64(%rip), %rax - movl %eax, 4(%rsp) - movl %eax, 2(%rax) /* Fixup the gdt base address */ - leaq efi32_boot_gdt(%rip), %rax - movl %eax, (%rsp) - - call __efi64_thunk - - addq $16, %rsp - pop %rbx - pop %rbp - ret -ENDPROC(efi64_thunk) -#endif /* CONFIG_EFI_MIXED */ diff --git a/arch/x86/boot/compressed/efi_thunk_64.S b/arch/x86/boot/compressed/efi_thunk_64.S new file mode 100644 index 00000000000000..630384a4c14a96 --- /dev/null +++ b/arch/x86/boot/compressed/efi_thunk_64.S @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2014, 2015 Intel Corporation; author Matt Fleming + * + * Early support for invoking 32-bit EFI services from a 64-bit kernel. + * + * Because this thunking occurs before ExitBootServices() we have to + * restore the firmware's 32-bit GDT before we make EFI serivce calls, + * since the firmware's 32-bit IDT is still currently installed and it + * needs to be able to service interrupts. + * + * On the plus side, we don't have to worry about mangling 64-bit + * addresses into 32-bits because we're executing with an identify + * mapped pagetable and haven't transitioned to 64-bit virtual addresses + * yet. + */ + +#include +#include +#include +#include +#include + + .code64 + .text +ENTRY(efi64_thunk) + push %rbp + push %rbx + + subq $8, %rsp + leaq efi_exit32(%rip), %rax + movl %eax, 4(%rsp) + leaq efi_gdt64(%rip), %rax + movl %eax, (%rsp) + movl %eax, 2(%rax) /* Fixup the gdt base address */ + + movl %ds, %eax + push %rax + movl %es, %eax + push %rax + movl %ss, %eax + push %rax + + /* + * Convert x86-64 ABI params to i386 ABI + */ + subq $32, %rsp + movl %esi, 0x0(%rsp) + movl %edx, 0x4(%rsp) + movl %ecx, 0x8(%rsp) + movq %r8, %rsi + movl %esi, 0xc(%rsp) + movq %r9, %rsi + movl %esi, 0x10(%rsp) + + sgdt save_gdt(%rip) + + leaq 1f(%rip), %rbx + movq %rbx, func_rt_ptr(%rip) + + /* + * Switch to gdt with 32-bit segments. This is the firmware GDT + * that was installed when the kernel started executing. This + * pointer was saved at the EFI stub entry point in head_64.S. + */ + leaq efi32_boot_gdt(%rip), %rax + lgdt (%rax) + + pushq $__KERNEL_CS + leaq efi_enter32(%rip), %rax + pushq %rax + lretq + +1: addq $32, %rsp + + lgdt save_gdt(%rip) + + pop %rbx + movl %ebx, %ss + pop %rbx + movl %ebx, %es + pop %rbx + movl %ebx, %ds + + /* + * Convert 32-bit status code into 64-bit. + */ + test %rax, %rax + jz 1f + movl %eax, %ecx + andl $0x0fffffff, %ecx + andl $0xf0000000, %eax + shl $32, %rax + or %rcx, %rax +1: + addq $8, %rsp + pop %rbx + pop %rbp + ret +ENDPROC(efi64_thunk) + +ENTRY(efi_exit32) + movq func_rt_ptr(%rip), %rax + push %rax + mov %rdi, %rax + ret +ENDPROC(efi_exit32) + + .code32 +/* + * EFI service pointer must be in %edi. + * + * The stack should represent the 32-bit calling convention. + */ +ENTRY(efi_enter32) + movl $__KERNEL_DS, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %ss + + /* Reload pgtables */ + movl %cr3, %eax + movl %eax, %cr3 + + /* Disable paging */ + movl %cr0, %eax + btrl $X86_CR0_PG_BIT, %eax + movl %eax, %cr0 + + /* Disable long mode via EFER */ + movl $MSR_EFER, %ecx + rdmsr + btrl $_EFER_LME, %eax + wrmsr + + call *%edi + + /* We must preserve return value */ + movl %eax, %edi + + /* + * Some firmware will return with interrupts enabled. Be sure to + * disable them before we switch GDTs. + */ + cli + + movl 56(%esp), %eax + movl %eax, 2(%eax) + lgdtl (%eax) + + movl %cr4, %eax + btsl $(X86_CR4_PAE_BIT), %eax + movl %eax, %cr4 + + movl %cr3, %eax + movl %eax, %cr3 + + movl $MSR_EFER, %ecx + rdmsr + btsl $_EFER_LME, %eax + wrmsr + + xorl %eax, %eax + lldt %ax + + movl 60(%esp), %eax + pushl $__KERNEL_CS + pushl %eax + + /* Enable paging */ + movl %cr0, %eax + btsl $X86_CR0_PG_BIT, %eax + movl %eax, %cr0 + lret +ENDPROC(efi_enter32) + + .data + .balign 8 + .global efi32_boot_gdt +efi32_boot_gdt: .word 0 + .quad 0 + +save_gdt: .word 0 + .quad 0 +func_rt_ptr: .quad 0 + + .global efi_gdt64 +efi_gdt64: + .word efi_gdt64_end - efi_gdt64 + .long 0 /* Filled out by user */ + .word 0 + .quad 0x0000000000000000 /* NULL descriptor */ + .quad 0x00af9a000000ffff /* __KERNEL_CS */ + .quad 0x00cf92000000ffff /* __KERNEL_DS */ + .quad 0x0080890000000000 /* TS descriptor */ + .quad 0x0000000000000000 /* TS continued */ +efi_gdt64_end: diff --git a/arch/x86/platform/efi/efi_stub_64.S b/arch/x86/platform/efi/efi_stub_64.S index 5fcda7272550a7..86d0f9e08dd95e 100644 --- a/arch/x86/platform/efi/efi_stub_64.S +++ b/arch/x86/platform/efi/efi_stub_64.S @@ -91,167 +91,6 @@ ENTRY(efi_call) ret ENDPROC(efi_call) -#ifdef CONFIG_EFI_MIXED - -/* - * We run this function from the 1:1 mapping. - * - * This function must be invoked with a 1:1 mapped stack. - */ -ENTRY(__efi64_thunk) - movl %ds, %eax - push %rax - movl %es, %eax - push %rax - movl %ss, %eax - push %rax - - subq $32, %rsp - movl %esi, 0x0(%rsp) - movl %edx, 0x4(%rsp) - movl %ecx, 0x8(%rsp) - movq %r8, %rsi - movl %esi, 0xc(%rsp) - movq %r9, %rsi - movl %esi, 0x10(%rsp) - - sgdt save_gdt(%rip) - - leaq 1f(%rip), %rbx - movq %rbx, func_rt_ptr(%rip) - - /* Switch to gdt with 32-bit segments */ - movl 64(%rsp), %eax - lgdt (%rax) - - leaq efi_enter32(%rip), %rax - pushq $__KERNEL_CS - pushq %rax - lretq - -1: addq $32, %rsp - - lgdt save_gdt(%rip) - - pop %rbx - movl %ebx, %ss - pop %rbx - movl %ebx, %es - pop %rbx - movl %ebx, %ds - - /* - * Convert 32-bit status code into 64-bit. - */ - test %rax, %rax - jz 1f - movl %eax, %ecx - andl $0x0fffffff, %ecx - andl $0xf0000000, %eax - shl $32, %rax - or %rcx, %rax -1: - ret -ENDPROC(__efi64_thunk) - -ENTRY(efi_exit32) - movq func_rt_ptr(%rip), %rax - push %rax - mov %rdi, %rax - ret -ENDPROC(efi_exit32) - - .code32 -/* - * EFI service pointer must be in %edi. - * - * The stack should represent the 32-bit calling convention. - */ -ENTRY(efi_enter32) - movl $__KERNEL_DS, %eax - movl %eax, %ds - movl %eax, %es - movl %eax, %ss - - /* Reload pgtables */ - movl %cr3, %eax - movl %eax, %cr3 - - /* Disable paging */ - movl %cr0, %eax - btrl $X86_CR0_PG_BIT, %eax - movl %eax, %cr0 - - /* Disable long mode via EFER */ - movl $MSR_EFER, %ecx - rdmsr - btrl $_EFER_LME, %eax - wrmsr - - call *%edi - - /* We must preserve return value */ - movl %eax, %edi - - /* - * Some firmware will return with interrupts enabled. Be sure to - * disable them before we switch GDTs. - */ - cli - - movl 68(%esp), %eax - movl %eax, 2(%eax) - lgdtl (%eax) - - movl %cr4, %eax - btsl $(X86_CR4_PAE_BIT), %eax - movl %eax, %cr4 - - movl %cr3, %eax - movl %eax, %cr3 - - movl $MSR_EFER, %ecx - rdmsr - btsl $_EFER_LME, %eax - wrmsr - - xorl %eax, %eax - lldt %ax - - movl 72(%esp), %eax - pushl $__KERNEL_CS - pushl %eax - - /* Enable paging */ - movl %cr0, %eax - btsl $X86_CR0_PG_BIT, %eax - movl %eax, %cr0 - lret -ENDPROC(efi_enter32) - - .data - .balign 8 - .global efi32_boot_gdt -efi32_boot_gdt: .word 0 - .quad 0 - -save_gdt: .word 0 - .quad 0 -func_rt_ptr: .quad 0 - - .global efi_gdt64 -efi_gdt64: - .word efi_gdt64_end - efi_gdt64 - .long 0 /* Filled out by user */ - .word 0 - .quad 0x0000000000000000 /* NULL descriptor */ - .quad 0x00af9a000000ffff /* __KERNEL_CS */ - .quad 0x00cf92000000ffff /* __KERNEL_DS */ - .quad 0x0080890000000000 /* TS descriptor */ - .quad 0x0000000000000000 /* TS continued */ -efi_gdt64_end: -#endif /* CONFIG_EFI_MIXED */ - .data ENTRY(efi_scratch) .fill 3,8,0 diff --git a/arch/x86/platform/efi/efi_thunk_64.S b/arch/x86/platform/efi/efi_thunk_64.S index 8806fa73e6e6d2..ff85d28c50f261 100644 --- a/arch/x86/platform/efi/efi_thunk_64.S +++ b/arch/x86/platform/efi/efi_thunk_64.S @@ -1,9 +1,26 @@ /* * Copyright (C) 2014 Intel Corporation; author Matt Fleming + * + * Support for invoking 32-bit EFI runtime services from a 64-bit + * kernel. + * + * The below thunking functions are only used after ExitBootServices() + * has been called. This simplifies things considerably as compared with + * the early EFI thunking because we can leave all the kernel state + * intact (GDT, IDT, etc) and simply invoke the the 32-bit EFI runtime + * services from __KERNEL32_CS. This means we can continue to service + * interrupts across an EFI mixed mode call. + * + * We do however, need to handle the fact that we're running in a full + * 64-bit virtual address space. Things like the stack and instruction + * addresses need to be accessible by the 32-bit firmware, so we rely on + * using the identity mappings in the EFI page table to access the stack + * and kernel text (see efi_setup_page_tables()). */ #include #include +#include .text .code64 @@ -33,14 +50,6 @@ ENTRY(efi64_thunk) leaq efi_exit32(%rip), %rbx subq %rax, %rbx movl %ebx, 8(%rsp) - leaq efi_gdt64(%rip), %rbx - subq %rax, %rbx - movl %ebx, 2(%ebx) - movl %ebx, 4(%rsp) - leaq efi_gdt32(%rip), %rbx - subq %rax, %rbx - movl %ebx, 2(%ebx) - movl %ebx, (%rsp) leaq __efi64_thunk(%rip), %rbx subq %rax, %rbx @@ -52,14 +61,92 @@ ENTRY(efi64_thunk) retq ENDPROC(efi64_thunk) - .data -efi_gdt32: - .word efi_gdt32_end - efi_gdt32 - .long 0 /* Filled out above */ - .word 0 - .quad 0x0000000000000000 /* NULL descriptor */ - .quad 0x00cf9a000000ffff /* __KERNEL_CS */ - .quad 0x00cf93000000ffff /* __KERNEL_DS */ -efi_gdt32_end: +/* + * We run this function from the 1:1 mapping. + * + * This function must be invoked with a 1:1 mapped stack. + */ +ENTRY(__efi64_thunk) + movl %ds, %eax + push %rax + movl %es, %eax + push %rax + movl %ss, %eax + push %rax + + subq $32, %rsp + movl %esi, 0x0(%rsp) + movl %edx, 0x4(%rsp) + movl %ecx, 0x8(%rsp) + movq %r8, %rsi + movl %esi, 0xc(%rsp) + movq %r9, %rsi + movl %esi, 0x10(%rsp) + + leaq 1f(%rip), %rbx + movq %rbx, func_rt_ptr(%rip) + + /* Switch to 32-bit descriptor */ + pushq $__KERNEL32_CS + leaq efi_enter32(%rip), %rax + pushq %rax + lretq + +1: addq $32, %rsp + + pop %rbx + movl %ebx, %ss + pop %rbx + movl %ebx, %es + pop %rbx + movl %ebx, %ds + /* + * Convert 32-bit status code into 64-bit. + */ + test %rax, %rax + jz 1f + movl %eax, %ecx + andl $0x0fffffff, %ecx + andl $0xf0000000, %eax + shl $32, %rax + or %rcx, %rax +1: + ret +ENDPROC(__efi64_thunk) + +ENTRY(efi_exit32) + movq func_rt_ptr(%rip), %rax + push %rax + mov %rdi, %rax + ret +ENDPROC(efi_exit32) + + .code32 +/* + * EFI service pointer must be in %edi. + * + * The stack should represent the 32-bit calling convention. + */ +ENTRY(efi_enter32) + movl $__KERNEL_DS, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %ss + + call *%edi + + /* We must preserve return value */ + movl %eax, %edi + + movl 72(%esp), %eax + pushl $__KERNEL_CS + pushl %eax + + lret +ENDPROC(efi_enter32) + + .data + .balign 8 +func_rt_ptr: .quad 0 efi_saved_sp: .quad 0 From 2f455ae12905fd211fd9440273dd06758997c9f2 Mon Sep 17 00:00:00 2001 From: Hector Marco-Gisbert Date: Sat, 14 Feb 2015 09:33:50 -0800 Subject: [PATCH 168/788] x86, mm/ASLR: Fix stack randomization on 64-bit systems commit 4e7c22d447bb6d7e37bfe39ff658486ae78e8d77 upstream. The issue is that the stack for processes is not properly randomized on 64 bit architectures due to an integer overflow. The affected function is randomize_stack_top() in file "fs/binfmt_elf.c": static unsigned long randomize_stack_top(unsigned long stack_top) { unsigned int random_variable = 0; if ((current->flags & PF_RANDOMIZE) && !(current->personality & ADDR_NO_RANDOMIZE)) { random_variable = get_random_int() & STACK_RND_MASK; random_variable <<= PAGE_SHIFT; } return PAGE_ALIGN(stack_top) + random_variable; return PAGE_ALIGN(stack_top) - random_variable; } Note that, it declares the "random_variable" variable as "unsigned int". Since the result of the shifting operation between STACK_RND_MASK (which is 0x3fffff on x86_64, 22 bits) and PAGE_SHIFT (which is 12 on x86_64): random_variable <<= PAGE_SHIFT; then the two leftmost bits are dropped when storing the result in the "random_variable". This variable shall be at least 34 bits long to hold the (22+12) result. These two dropped bits have an impact on the entropy of process stack. Concretely, the total stack entropy is reduced by four: from 2^28 to 2^30 (One fourth of expected entropy). This patch restores back the entropy by correcting the types involved in the operations in the functions randomize_stack_top() and stack_maxrandom_size(). The successful fix can be tested with: $ for i in `seq 1 10`; do cat /proc/self/maps | grep stack; done 7ffeda566000-7ffeda587000 rw-p 00000000 00:00 0 [stack] 7fff5a332000-7fff5a353000 rw-p 00000000 00:00 0 [stack] 7ffcdb7a1000-7ffcdb7c2000 rw-p 00000000 00:00 0 [stack] 7ffd5e2c4000-7ffd5e2e5000 rw-p 00000000 00:00 0 [stack] ... Once corrected, the leading bytes should be between 7ffc and 7fff, rather than always being 7fff. Signed-off-by: Hector Marco-Gisbert Signed-off-by: Ismael Ripoll [ Rebased, fixed 80 char bugs, cleaned up commit message, added test example and CVE ] Signed-off-by: Kees Cook Cc: Linus Torvalds Cc: Andrew Morton Cc: Al Viro Fixes: CVE-2015-1593 Link: http://lkml.kernel.org/r/20150214173350.GA18393@www.outflux.net Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- arch/x86/mm/mmap.c | 6 +++--- fs/binfmt_elf.c | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/arch/x86/mm/mmap.c b/arch/x86/mm/mmap.c index 919b91205cd4be..df4552bd239e03 100644 --- a/arch/x86/mm/mmap.c +++ b/arch/x86/mm/mmap.c @@ -35,12 +35,12 @@ struct va_alignment __read_mostly va_align = { .flags = -1, }; -static unsigned int stack_maxrandom_size(void) +static unsigned long stack_maxrandom_size(void) { - unsigned int max = 0; + unsigned long max = 0; if ((current->flags & PF_RANDOMIZE) && !(current->personality & ADDR_NO_RANDOMIZE)) { - max = ((-1U) & STACK_RND_MASK) << PAGE_SHIFT; + max = ((-1UL) & STACK_RND_MASK) << PAGE_SHIFT; } return max; diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 02b16910f4c9d5..995986b8e36b8f 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -645,11 +645,12 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex, static unsigned long randomize_stack_top(unsigned long stack_top) { - unsigned int random_variable = 0; + unsigned long random_variable = 0; if ((current->flags & PF_RANDOMIZE) && !(current->personality & ADDR_NO_RANDOMIZE)) { - random_variable = get_random_int() & STACK_RND_MASK; + random_variable = (unsigned long) get_random_int(); + random_variable &= STACK_RND_MASK; random_variable <<= PAGE_SHIFT; } #ifdef CONFIG_STACK_GROWSUP From 58557add70dcae1dd7df1d039c99f27edc7f4ece Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Mon, 16 Feb 2015 10:11:13 +0800 Subject: [PATCH 169/788] x86/irq: Fix regression caused by commit b568b8601f05 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 1ea76fbadd667b19c4fa4466f3a3b55a505e83d9 upstream. Commit b568b8601f05 ("Treat SCI interrupt as normal GSI interrupt") accidently removes support of legacy PIC interrupt when fixing a regression for Xen, which causes a nasty regression on HP/Compaq nc6000 where we fail to register the ACPI interrupt, and thus lose eg. thermal notifications leading a potentially overheated machine. So reintroduce support of legacy PIC based ACPI SCI interrupt. Reported-by: Ville Syrjälä Tested-by: Ville Syrjälä Signed-off-by: Jiang Liu Signed-off-by: Peter Zijlstra (Intel) Acked-by: Pavel Machek Cc: H. Peter Anvin Cc: Len Brown Cc: Linus Torvalds Cc: Rafael J. Wysocki Cc: Sander Eikelenboom Cc: linux-pm@vger.kernel.org Link: http://lkml.kernel.org/r/1424052673-22974-1-git-send-email-jiang.liu@linux.intel.com Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/acpi/boot.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c index b9e30daa0881b3..b5ddc964922716 100644 --- a/arch/x86/kernel/acpi/boot.c +++ b/arch/x86/kernel/acpi/boot.c @@ -613,6 +613,11 @@ int acpi_gsi_to_irq(u32 gsi, unsigned int *irqp) { int rc, irq, trigger, polarity; + if (acpi_irq_model == ACPI_IRQ_MODEL_PIC) { + *irqp = gsi; + return 0; + } + rc = acpi_get_override_irq(gsi, &trigger, &polarity); if (rc == 0) { trigger = trigger ? ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE; From db8b5886cf08a4890e920b1fae8cbf21608cfbcc Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 14 Jan 2015 18:39:31 +0200 Subject: [PATCH 170/788] x86: pmc-atom: Assign debugfs node as soon as possible commit 1b43d7125f3b6f7d46e72da64f65f3187a83b66b upstream. pmc_dbgfs_unregister() will be called when pmc->dbgfs_dir is unconditionally NULL on error path in pmc_dbgfs_register(). To prevent this we move the assignment to where is should be. Fixes: f855911c1f48 (x86/pmc_atom: Expose PMC device state and platform sleep state) Reported-by: Thomas Gleixner Signed-off-by: Andy Shevchenko Cc: Aubrey Li Cc: Rafael J. Wysocki Cc: Kumar P. Mahesh Link: http://lkml.kernel.org/r/1421253575-22509-2-git-send-email-andriy.shevchenko@linux.intel.com Signed-off-by: Thomas Gleixner Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/pmc_atom.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/pmc_atom.c b/arch/x86/kernel/pmc_atom.c index 0ee5025e0fa4cf..8bb9a611ca2337 100644 --- a/arch/x86/kernel/pmc_atom.c +++ b/arch/x86/kernel/pmc_atom.c @@ -217,6 +217,8 @@ static int pmc_dbgfs_register(struct pmc_dev *pmc, struct pci_dev *pdev) if (!dir) return -ENOMEM; + pmc->dbgfs_dir = dir; + f = debugfs_create_file("dev_state", S_IFREG | S_IRUGO, dir, pmc, &pmc_dev_state_ops); if (!f) { @@ -229,7 +231,7 @@ static int pmc_dbgfs_register(struct pmc_dev *pmc, struct pci_dev *pdev) dev_err(&pdev->dev, "sleep_state register failed\n"); goto err; } - pmc->dbgfs_dir = dir; + return 0; err: pmc_dbgfs_unregister(pmc); From 02c3106b329bae407e8ce994fb7995dc854ca4e8 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Tue, 25 Nov 2014 10:16:39 +0100 Subject: [PATCH 171/788] kernel: tighten rules for ACCESS ONCE commit 927609d622a3773995f84bc03b4564f873cf0e22 upstream. Now that all non-scalar users of ACCESS_ONCE have been converted to READ_ONCE or ASSIGN once, lets tighten ACCESS_ONCE to only work on scalar types. This variant was proposed by Alexei Starovoitov. Signed-off-by: Christian Borntraeger Reviewed-by: Paul E. McKenney Signed-off-by: Greg Kroah-Hartman --- include/linux/compiler.h | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/include/linux/compiler.h b/include/linux/compiler.h index 33063f872ee3cd..4a0e80d09a81e6 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h @@ -447,12 +447,23 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s * to make the compiler aware of ordering is to put the two invocations of * ACCESS_ONCE() in different C statements. * - * This macro does absolutely -nothing- to prevent the CPU from reordering, - * merging, or refetching absolutely anything at any time. Its main intended - * use is to mediate communication between process-level code and irq/NMI - * handlers, all running on the same CPU. + * ACCESS_ONCE will only work on scalar types. For union types, ACCESS_ONCE + * on a union member will work as long as the size of the member matches the + * size of the union and the size is smaller than word size. + * + * The major use cases of ACCESS_ONCE used to be (1) Mediating communication + * between process-level code and irq/NMI handlers, all running on the same CPU, + * and (2) Ensuring that the compiler does not fold, spindle, or otherwise + * mutilate accesses that either do not require ordering or that interact + * with an explicit memory barrier or atomic instruction that provides the + * required ordering. + * + * If possible use READ_ONCE/ASSIGN_ONCE instead. */ -#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x)) +#define __ACCESS_ONCE(x) ({ \ + __maybe_unused typeof(x) __var = 0; \ + (volatile typeof(x) *)&(x); }) +#define ACCESS_ONCE(x) (*__ACCESS_ONCE(x)) /* Ignore/forbid kprobes attach on very low level functions marked by this attribute: */ #ifdef CONFIG_KPROBES From fec1418da2939d368d934d787f340dd667edc5b7 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Mon, 12 Jan 2015 12:13:39 +0100 Subject: [PATCH 172/788] kernel: Fix sparse warning for ACCESS_ONCE commit c5b19946eb76c67566aae6a84bf2b10ad59295ea upstream. Commit 927609d622a3 ("kernel: tighten rules for ACCESS ONCE") results in sparse warnings like "Using plain integer as NULL pointer" - Let's add a type cast to the dummy assignment. To avoid warnings lik "sparse: warning: cast to restricted __hc32" we also use __force on that cast. Fixes: 927609d622a3 ("kernel: tighten rules for ACCESS ONCE") Signed-off-by: Christian Borntraeger Signed-off-by: Greg Kroah-Hartman --- include/linux/compiler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/compiler.h b/include/linux/compiler.h index 4a0e80d09a81e6..0b203111c16606 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h @@ -461,7 +461,7 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s * If possible use READ_ONCE/ASSIGN_ONCE instead. */ #define __ACCESS_ONCE(x) ({ \ - __maybe_unused typeof(x) __var = 0; \ + __maybe_unused typeof(x) __var = (__force typeof(x)) 0; \ (volatile typeof(x) *)&(x); }) #define ACCESS_ONCE(x) (*__ACCESS_ONCE(x)) From b6888abd998c2ac2d2a3291cc205556762dda581 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 20 Feb 2015 15:46:31 -0800 Subject: [PATCH 173/788] kernel: make READ_ONCE() valid on const arguments commit dd36929720f40f17685e841ae0d4c581c165ea60 upstream. The use of READ_ONCE() causes lots of warnings witht he pending paravirt spinlock fixes, because those ends up having passing a member to a 'const' structure to READ_ONCE(). There should certainly be nothing wrong with using READ_ONCE() with a const source, but the helper function __read_once_size() would cause warnings because it would drop the 'const' qualifier, but also because the destination would be marked 'const' too due to the use of 'typeof'. Use a union of types in READ_ONCE() to avoid this issue. Also make sure to use parenthesis around the macro arguments to avoid possible operator precedence issues. Tested-by: Ingo Molnar Cc: Christian Borntraeger Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- include/linux/compiler.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/linux/compiler.h b/include/linux/compiler.h index 0b203111c16606..fa6a31441d0425 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h @@ -198,7 +198,7 @@ static __always_inline void data_access_exceeds_word_size(void) { } -static __always_inline void __read_once_size(volatile void *p, void *res, int size) +static __always_inline void __read_once_size(const volatile void *p, void *res, int size) { switch (size) { case 1: *(__u8 *)res = *(volatile __u8 *)p; break; @@ -255,10 +255,10 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s */ #define READ_ONCE(x) \ - ({ typeof(x) __val; __read_once_size(&x, &__val, sizeof(__val)); __val; }) + ({ union { typeof(x) __val; char __c[1]; } __u; __read_once_size(&(x), __u.__c, sizeof(x)); __u.__val; }) #define WRITE_ONCE(x, val) \ - ({ typeof(x) __val; __val = val; __write_once_size(&x, &__val, sizeof(__val)); __val; }) + ({ typeof(x) __val = (val); __write_once_size(&(x), &__val, sizeof(__val)); __val; }) #endif /* __KERNEL__ */ From dcc2e51a1fc91c2a631ed3ea5afed22c6aa13728 Mon Sep 17 00:00:00 2001 From: Raghavendra K T Date: Fri, 6 Feb 2015 16:44:11 +0530 Subject: [PATCH 174/788] x86/spinlocks/paravirt: Fix memory corruption on unlock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit d6abfdb2022368d8c6c4be3f11a06656601a6cc2 upstream. Paravirt spinlock clears slowpath flag after doing unlock. As explained by Linus currently it does: prev = *lock; add_smp(&lock->tickets.head, TICKET_LOCK_INC); /* add_smp() is a full mb() */ if (unlikely(lock->tickets.tail & TICKET_SLOWPATH_FLAG)) __ticket_unlock_slowpath(lock, prev); which is *exactly* the kind of things you cannot do with spinlocks, because after you've done the "add_smp()" and released the spinlock for the fast-path, you can't access the spinlock any more. Exactly because a fast-path lock might come in, and release the whole data structure. Linus suggested that we should not do any writes to lock after unlock(), and we can move slowpath clearing to fastpath lock. So this patch implements the fix with: 1. Moving slowpath flag to head (Oleg): Unlocked locks don't care about the slowpath flag; therefore we can keep it set after the last unlock, and clear it again on the first (try)lock. -- this removes the write after unlock. note that keeping slowpath flag would result in unnecessary kicks. By moving the slowpath flag from the tail to the head ticket we also avoid the need to access both the head and tail tickets on unlock. 2. use xadd to avoid read/write after unlock that checks the need for unlock_kick (Linus): We further avoid the need for a read-after-release by using xadd; the prev head value will include the slowpath flag and indicate if we need to do PV kicking of suspended spinners -- on modern chips xadd isn't (much) more expensive than an add + load. Result: setup: 16core (32 cpu +ht sandy bridge 8GB 16vcpu guest) benchmark overcommit %improve kernbench 1x -0.13 kernbench 2x 0.02 dbench 1x -1.77 dbench 2x -0.63 [Jeremy: Hinted missing TICKET_LOCK_INC for kick] [Oleg: Moved slowpath flag to head, ticket_equals idea] [PeterZ: Added detailed changelog] Suggested-by: Linus Torvalds Reported-by: Sasha Levin Tested-by: Sasha Levin Signed-off-by: Raghavendra K T Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Oleg Nesterov Cc: Andrew Jones Cc: Andrew Morton Cc: Andy Lutomirski Cc: Boris Ostrovsky Cc: Christian Borntraeger Cc: Christoph Lameter Cc: Dave Hansen Cc: Dave Jones Cc: David Vrabel Cc: Fernando Luis Vázquez Cao Cc: Konrad Rzeszutek Wilk Cc: Masami Hiramatsu Cc: Paolo Bonzini Cc: Paul E. McKenney Cc: Ulrich Obergfell Cc: Waiman Long Cc: a.ryabinin@samsung.com Cc: dave@stgolabs.net Cc: hpa@zytor.com Cc: jasowang@redhat.com Cc: jeremy@goop.org Cc: paul.gortmaker@windriver.com Cc: riel@redhat.com Cc: tglx@linutronix.de Cc: waiman.long@hp.com Cc: xen-devel@lists.xenproject.org Link: http://lkml.kernel.org/r/20150215173043.GA7471@linux.vnet.ibm.com Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/spinlock.h | 94 ++++++++++++++++----------------- arch/x86/kernel/kvm.c | 13 +++-- arch/x86/xen/spinlock.c | 13 +++-- 3 files changed, 64 insertions(+), 56 deletions(-) diff --git a/arch/x86/include/asm/spinlock.h b/arch/x86/include/asm/spinlock.h index 625660f8a2fcf0..cf87de3fc39000 100644 --- a/arch/x86/include/asm/spinlock.h +++ b/arch/x86/include/asm/spinlock.h @@ -46,7 +46,7 @@ static __always_inline bool static_key_false(struct static_key *key); static inline void __ticket_enter_slowpath(arch_spinlock_t *lock) { - set_bit(0, (volatile unsigned long *)&lock->tickets.tail); + set_bit(0, (volatile unsigned long *)&lock->tickets.head); } #else /* !CONFIG_PARAVIRT_SPINLOCKS */ @@ -60,10 +60,30 @@ static inline void __ticket_unlock_kick(arch_spinlock_t *lock, } #endif /* CONFIG_PARAVIRT_SPINLOCKS */ +static inline int __tickets_equal(__ticket_t one, __ticket_t two) +{ + return !((one ^ two) & ~TICKET_SLOWPATH_FLAG); +} + +static inline void __ticket_check_and_clear_slowpath(arch_spinlock_t *lock, + __ticket_t head) +{ + if (head & TICKET_SLOWPATH_FLAG) { + arch_spinlock_t old, new; + + old.tickets.head = head; + new.tickets.head = head & ~TICKET_SLOWPATH_FLAG; + old.tickets.tail = new.tickets.head + TICKET_LOCK_INC; + new.tickets.tail = old.tickets.tail; + + /* try to clear slowpath flag when there are no contenders */ + cmpxchg(&lock->head_tail, old.head_tail, new.head_tail); + } +} static __always_inline int arch_spin_value_unlocked(arch_spinlock_t lock) { - return lock.tickets.head == lock.tickets.tail; + return __tickets_equal(lock.tickets.head, lock.tickets.tail); } /* @@ -87,18 +107,21 @@ static __always_inline void arch_spin_lock(arch_spinlock_t *lock) if (likely(inc.head == inc.tail)) goto out; - inc.tail &= ~TICKET_SLOWPATH_FLAG; for (;;) { unsigned count = SPIN_THRESHOLD; do { - if (READ_ONCE(lock->tickets.head) == inc.tail) - goto out; + inc.head = READ_ONCE(lock->tickets.head); + if (__tickets_equal(inc.head, inc.tail)) + goto clear_slowpath; cpu_relax(); } while (--count); __ticket_lock_spinning(lock, inc.tail); } -out: barrier(); /* make sure nothing creeps before the lock is taken */ +clear_slowpath: + __ticket_check_and_clear_slowpath(lock, inc.head); +out: + barrier(); /* make sure nothing creeps before the lock is taken */ } static __always_inline int arch_spin_trylock(arch_spinlock_t *lock) @@ -106,56 +129,30 @@ static __always_inline int arch_spin_trylock(arch_spinlock_t *lock) arch_spinlock_t old, new; old.tickets = READ_ONCE(lock->tickets); - if (old.tickets.head != (old.tickets.tail & ~TICKET_SLOWPATH_FLAG)) + if (!__tickets_equal(old.tickets.head, old.tickets.tail)) return 0; new.head_tail = old.head_tail + (TICKET_LOCK_INC << TICKET_SHIFT); + new.head_tail &= ~TICKET_SLOWPATH_FLAG; /* cmpxchg is a full barrier, so nothing can move before it */ return cmpxchg(&lock->head_tail, old.head_tail, new.head_tail) == old.head_tail; } -static inline void __ticket_unlock_slowpath(arch_spinlock_t *lock, - arch_spinlock_t old) -{ - arch_spinlock_t new; - - BUILD_BUG_ON(((__ticket_t)NR_CPUS) != NR_CPUS); - - /* Perform the unlock on the "before" copy */ - old.tickets.head += TICKET_LOCK_INC; - - /* Clear the slowpath flag */ - new.head_tail = old.head_tail & ~(TICKET_SLOWPATH_FLAG << TICKET_SHIFT); - - /* - * If the lock is uncontended, clear the flag - use cmpxchg in - * case it changes behind our back though. - */ - if (new.tickets.head != new.tickets.tail || - cmpxchg(&lock->head_tail, old.head_tail, - new.head_tail) != old.head_tail) { - /* - * Lock still has someone queued for it, so wake up an - * appropriate waiter. - */ - __ticket_unlock_kick(lock, old.tickets.head); - } -} - static __always_inline void arch_spin_unlock(arch_spinlock_t *lock) { if (TICKET_SLOWPATH_FLAG && - static_key_false(¶virt_ticketlocks_enabled)) { - arch_spinlock_t prev; + static_key_false(¶virt_ticketlocks_enabled)) { + __ticket_t head; - prev = *lock; - add_smp(&lock->tickets.head, TICKET_LOCK_INC); + BUILD_BUG_ON(((__ticket_t)NR_CPUS) != NR_CPUS); - /* add_smp() is a full mb() */ + head = xadd(&lock->tickets.head, TICKET_LOCK_INC); - if (unlikely(lock->tickets.tail & TICKET_SLOWPATH_FLAG)) - __ticket_unlock_slowpath(lock, prev); + if (unlikely(head & TICKET_SLOWPATH_FLAG)) { + head &= ~TICKET_SLOWPATH_FLAG; + __ticket_unlock_kick(lock, (head + TICKET_LOCK_INC)); + } } else __add(&lock->tickets.head, TICKET_LOCK_INC, UNLOCK_LOCK_PREFIX); } @@ -164,14 +161,15 @@ static inline int arch_spin_is_locked(arch_spinlock_t *lock) { struct __raw_tickets tmp = READ_ONCE(lock->tickets); - return tmp.tail != tmp.head; + return !__tickets_equal(tmp.tail, tmp.head); } static inline int arch_spin_is_contended(arch_spinlock_t *lock) { struct __raw_tickets tmp = READ_ONCE(lock->tickets); - return (__ticket_t)(tmp.tail - tmp.head) > TICKET_LOCK_INC; + tmp.head &= ~TICKET_SLOWPATH_FLAG; + return (tmp.tail - tmp.head) > TICKET_LOCK_INC; } #define arch_spin_is_contended arch_spin_is_contended @@ -183,16 +181,16 @@ static __always_inline void arch_spin_lock_flags(arch_spinlock_t *lock, static inline void arch_spin_unlock_wait(arch_spinlock_t *lock) { - __ticket_t head = ACCESS_ONCE(lock->tickets.head); + __ticket_t head = READ_ONCE(lock->tickets.head); for (;;) { - struct __raw_tickets tmp = ACCESS_ONCE(lock->tickets); + struct __raw_tickets tmp = READ_ONCE(lock->tickets); /* * We need to check "unlocked" in a loop, tmp.head == head * can be false positive because of overflow. */ - if (tmp.head == (tmp.tail & ~TICKET_SLOWPATH_FLAG) || - tmp.head != head) + if (__tickets_equal(tmp.head, tmp.tail) || + !__tickets_equal(tmp.head, head)) break; cpu_relax(); diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c index 94f6434843008c..e354cc6446aba4 100644 --- a/arch/x86/kernel/kvm.c +++ b/arch/x86/kernel/kvm.c @@ -609,7 +609,7 @@ static inline void check_zero(void) u8 ret; u8 old; - old = ACCESS_ONCE(zero_stats); + old = READ_ONCE(zero_stats); if (unlikely(old)) { ret = cmpxchg(&zero_stats, old, 0); /* This ensures only one fellow resets the stat */ @@ -727,6 +727,7 @@ __visible void kvm_lock_spinning(struct arch_spinlock *lock, __ticket_t want) int cpu; u64 start; unsigned long flags; + __ticket_t head; if (in_nmi()) return; @@ -768,11 +769,15 @@ __visible void kvm_lock_spinning(struct arch_spinlock *lock, __ticket_t want) */ __ticket_enter_slowpath(lock); + /* make sure enter_slowpath, which is atomic does not cross the read */ + smp_mb__after_atomic(); + /* * check again make sure it didn't become free while * we weren't looking. */ - if (ACCESS_ONCE(lock->tickets.head) == want) { + head = READ_ONCE(lock->tickets.head); + if (__tickets_equal(head, want)) { add_stats(TAKEN_SLOW_PICKUP, 1); goto out; } @@ -803,8 +808,8 @@ static void kvm_unlock_kick(struct arch_spinlock *lock, __ticket_t ticket) add_stats(RELEASED_SLOW, 1); for_each_cpu(cpu, &waiting_cpus) { const struct kvm_lock_waiting *w = &per_cpu(klock_waiting, cpu); - if (ACCESS_ONCE(w->lock) == lock && - ACCESS_ONCE(w->want) == ticket) { + if (READ_ONCE(w->lock) == lock && + READ_ONCE(w->want) == ticket) { add_stats(RELEASED_SLOW_KICKED, 1); kvm_kick_cpu(cpu); break; diff --git a/arch/x86/xen/spinlock.c b/arch/x86/xen/spinlock.c index 23b45eb9a89ce4..956374c1edbc31 100644 --- a/arch/x86/xen/spinlock.c +++ b/arch/x86/xen/spinlock.c @@ -41,7 +41,7 @@ static u8 zero_stats; static inline void check_zero(void) { u8 ret; - u8 old = ACCESS_ONCE(zero_stats); + u8 old = READ_ONCE(zero_stats); if (unlikely(old)) { ret = cmpxchg(&zero_stats, old, 0); /* This ensures only one fellow resets the stat */ @@ -112,6 +112,7 @@ __visible void xen_lock_spinning(struct arch_spinlock *lock, __ticket_t want) struct xen_lock_waiting *w = this_cpu_ptr(&lock_waiting); int cpu = smp_processor_id(); u64 start; + __ticket_t head; unsigned long flags; /* If kicker interrupts not initialized yet, just spin */ @@ -159,11 +160,15 @@ __visible void xen_lock_spinning(struct arch_spinlock *lock, __ticket_t want) */ __ticket_enter_slowpath(lock); + /* make sure enter_slowpath, which is atomic does not cross the read */ + smp_mb__after_atomic(); + /* * check again make sure it didn't become free while * we weren't looking */ - if (ACCESS_ONCE(lock->tickets.head) == want) { + head = READ_ONCE(lock->tickets.head); + if (__tickets_equal(head, want)) { add_stats(TAKEN_SLOW_PICKUP, 1); goto out; } @@ -204,8 +209,8 @@ static void xen_unlock_kick(struct arch_spinlock *lock, __ticket_t next) const struct xen_lock_waiting *w = &per_cpu(lock_waiting, cpu); /* Make sure we read lock before want */ - if (ACCESS_ONCE(w->lock) == lock && - ACCESS_ONCE(w->want) == next) { + if (READ_ONCE(w->lock) == lock && + READ_ONCE(w->want) == next) { add_stats(RELEASED_SLOW_KICKED, 1); xen_send_IPI_one(cpu, XEN_SPIN_UNLOCK_VECTOR); break; From 24c94559aff487245e423acffd05bc751a43674f Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Sun, 7 Dec 2014 22:01:59 +0100 Subject: [PATCH 175/788] x86/xen/p2m: Replace ACCESS_ONCE with READ_ONCE commit 1760f1eb7ec485197bd3a8a9c13e4160bb740275 upstream. ACCESS_ONCE does not work reliably on non-scalar types. For example gcc 4.6 and 4.7 might remove the volatile tag for such accesses during the SRA (scalar replacement of aggregates) step (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58145) Change the p2m code to replace ACCESS_ONCE with READ_ONCE. Signed-off-by: Christian Borntraeger Reviewed-by: Juergen Gross Acked-by: David Vrabel Signed-off-by: Greg Kroah-Hartman --- arch/x86/xen/p2m.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/xen/p2m.c b/arch/x86/xen/p2m.c index 70fb5075c901f5..376a0a9dc670b4 100644 --- a/arch/x86/xen/p2m.c +++ b/arch/x86/xen/p2m.c @@ -554,7 +554,7 @@ static bool alloc_p2m(unsigned long pfn) mid_mfn = NULL; } - p2m_pfn = pte_pfn(ACCESS_ONCE(*ptep)); + p2m_pfn = pte_pfn(READ_ONCE(*ptep)); if (p2m_pfn == PFN_DOWN(__pa(p2m_identity)) || p2m_pfn == PFN_DOWN(__pa(p2m_missing))) { /* p2m leaf page is missing */ From e1adf6d7f02e9b25ac8bc618c29cae9f0d25cedb Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 9 Oct 2014 16:54:13 +0200 Subject: [PATCH 176/788] quota: Store maximum space limit in bytes commit b10a08194c2b615955dfab2300331a90ae9344c7 upstream. Currently maximum space limit quota format supports is in blocks however since we store space limits in bytes, this is somewhat confusing. So store the maximum limit in bytes as well. Also rename the field to match the new unit and related inode field to match the new naming scheme. Reviewed-by: Christoph Hellwig Signed-off-by: Jan Kara Signed-off-by: Greg Kroah-Hartman --- fs/ocfs2/quota_local.c | 4 ++-- fs/quota/dquot.c | 18 ++++-------------- fs/quota/quota_v1.c | 4 ++-- fs/quota/quota_v2.c | 10 +++++----- include/linux/quota.h | 4 ++-- 5 files changed, 15 insertions(+), 25 deletions(-) diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c index 10b653930ee26a..465223b7592ed0 100644 --- a/fs/ocfs2/quota_local.c +++ b/fs/ocfs2/quota_local.c @@ -701,8 +701,8 @@ static int ocfs2_local_read_info(struct super_block *sb, int type) /* We don't need the lock and we have to acquire quota file locks * which will later depend on this lock */ mutex_unlock(&sb_dqopt(sb)->dqio_mutex); - info->dqi_maxblimit = 0x7fffffffffffffffLL; - info->dqi_maxilimit = 0x7fffffffffffffffLL; + info->dqi_max_spc_limit = 0x7fffffffffffffffLL; + info->dqi_max_ino_limit = 0x7fffffffffffffffLL; oinfo = kmalloc(sizeof(struct ocfs2_mem_dqinfo), GFP_NOFS); if (!oinfo) { mlog(ML_ERROR, "failed to allocate memory for ocfs2 quota" diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index 69df5b239844f9..ca035a2fce810e 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -2385,16 +2385,6 @@ int dquot_quota_on_mount(struct super_block *sb, char *qf_name, } EXPORT_SYMBOL(dquot_quota_on_mount); -static inline qsize_t qbtos(qsize_t blocks) -{ - return blocks << QIF_DQBLKSIZE_BITS; -} - -static inline qsize_t stoqb(qsize_t space) -{ - return (space + QIF_DQBLKSIZE - 1) >> QIF_DQBLKSIZE_BITS; -} - /* Generic routine for getting common part of quota structure */ static void do_get_dqblk(struct dquot *dquot, struct qc_dqblk *di) { @@ -2444,13 +2434,13 @@ static int do_set_dqblk(struct dquot *dquot, struct qc_dqblk *di) return -EINVAL; if (((di->d_fieldmask & QC_SPC_SOFT) && - stoqb(di->d_spc_softlimit) > dqi->dqi_maxblimit) || + di->d_spc_softlimit > dqi->dqi_max_spc_limit) || ((di->d_fieldmask & QC_SPC_HARD) && - stoqb(di->d_spc_hardlimit) > dqi->dqi_maxblimit) || + di->d_spc_hardlimit > dqi->dqi_max_spc_limit) || ((di->d_fieldmask & QC_INO_SOFT) && - (di->d_ino_softlimit > dqi->dqi_maxilimit)) || + (di->d_ino_softlimit > dqi->dqi_max_ino_limit)) || ((di->d_fieldmask & QC_INO_HARD) && - (di->d_ino_hardlimit > dqi->dqi_maxilimit))) + (di->d_ino_hardlimit > dqi->dqi_max_ino_limit))) return -ERANGE; spin_lock(&dq_data_lock); diff --git a/fs/quota/quota_v1.c b/fs/quota/quota_v1.c index 469c6848b322dd..8fe79beced5cc0 100644 --- a/fs/quota/quota_v1.c +++ b/fs/quota/quota_v1.c @@ -169,8 +169,8 @@ static int v1_read_file_info(struct super_block *sb, int type) } ret = 0; /* limits are stored as unsigned 32-bit data */ - dqopt->info[type].dqi_maxblimit = 0xffffffff; - dqopt->info[type].dqi_maxilimit = 0xffffffff; + dqopt->info[type].dqi_max_spc_limit = 0xffffffffULL << QUOTABLOCK_BITS; + dqopt->info[type].dqi_max_ino_limit = 0xffffffff; dqopt->info[type].dqi_igrace = dqblk.dqb_itime ? dqblk.dqb_itime : MAX_IQ_TIME; dqopt->info[type].dqi_bgrace = diff --git a/fs/quota/quota_v2.c b/fs/quota/quota_v2.c index 02751ec695c596..d1a8054bba9a3d 100644 --- a/fs/quota/quota_v2.c +++ b/fs/quota/quota_v2.c @@ -117,12 +117,12 @@ static int v2_read_file_info(struct super_block *sb, int type) qinfo = info->dqi_priv; if (version == 0) { /* limits are stored as unsigned 32-bit data */ - info->dqi_maxblimit = 0xffffffff; - info->dqi_maxilimit = 0xffffffff; + info->dqi_max_spc_limit = 0xffffffffULL << QUOTABLOCK_BITS; + info->dqi_max_ino_limit = 0xffffffff; } else { - /* used space is stored as unsigned 64-bit value */ - info->dqi_maxblimit = 0xffffffffffffffffULL; /* 2^64-1 */ - info->dqi_maxilimit = 0xffffffffffffffffULL; + /* used space is stored as unsigned 64-bit value in bytes */ + info->dqi_max_spc_limit = 0xffffffffffffffffULL; /* 2^64-1 */ + info->dqi_max_ino_limit = 0xffffffffffffffffULL; } info->dqi_bgrace = le32_to_cpu(dinfo.dqi_bgrace); info->dqi_igrace = le32_to_cpu(dinfo.dqi_igrace); diff --git a/include/linux/quota.h b/include/linux/quota.h index 097d7eb2441e52..b86df497aba3a7 100644 --- a/include/linux/quota.h +++ b/include/linux/quota.h @@ -216,8 +216,8 @@ struct mem_dqinfo { unsigned long dqi_flags; unsigned int dqi_bgrace; unsigned int dqi_igrace; - qsize_t dqi_maxblimit; - qsize_t dqi_maxilimit; + qsize_t dqi_max_spc_limit; + qsize_t dqi_max_ino_limit; void *dqi_priv; }; From 74df55cf12da35361087869e3e4cb7e9e942d4f6 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 7 Jan 2015 12:32:28 -0800 Subject: [PATCH 177/788] next: sh: Fix compile error commit 378af02b1aecabb3756e19c0cbb8cdd9c3b9637f upstream. Commit 927609d622a3 ("kernel: tighten rules for ACCESS ONCE") results in a compile failure for sh builds with CONFIG_X2TLB enabled. arch/sh/mm/gup.c: In function 'gup_get_pte': arch/sh/mm/gup.c:20:2: error: invalid initializer make[1]: *** [arch/sh/mm/gup.o] Error 1 Replace ACCESS_ONCE with READ_ONCE to fix the problem. Fixes: 927609d622a3 ("kernel: tighten rules for ACCESS ONCE") Cc: Paul E. McKenney Cc: Christian Borntraeger Signed-off-by: Guenter Roeck Reviewed-by: Paul E. McKenney Signed-off-by: Christian Borntraeger Signed-off-by: Greg Kroah-Hartman --- arch/sh/mm/gup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/sh/mm/gup.c b/arch/sh/mm/gup.c index 37458f38b22093..e113bb429e8e4a 100644 --- a/arch/sh/mm/gup.c +++ b/arch/sh/mm/gup.c @@ -17,7 +17,7 @@ static inline pte_t gup_get_pte(pte_t *ptep) { #ifndef CONFIG_X2TLB - return ACCESS_ONCE(*ptep); + return READ_ONCE(*ptep); #else /* * With get_user_pages_fast, we walk down the pagetables without From dd4c10858bb00704df476ef17a0b3037b909e4f5 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Tue, 6 Jan 2015 22:54:46 +0100 Subject: [PATCH 178/788] mm/gup: Replace ACCESS_ONCE with READ_ONCE commit 38c5ce936a0862a6ce2c8d1c72689a3aba301425 upstream. ACCESS_ONCE does not work reliably on non-scalar types. For example gcc 4.6 and 4.7 might remove the volatile tag for such accesses during the SRA (scalar replacement of aggregates) step (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58145) Fixup gup_pmd_range. Signed-off-by: Christian Borntraeger Signed-off-by: Greg Kroah-Hartman --- mm/gup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/gup.c b/mm/gup.c index 8dd50ce6326fd5..9b2afbfe67e390 100644 --- a/mm/gup.c +++ b/mm/gup.c @@ -926,7 +926,7 @@ static int gup_pmd_range(pud_t pud, unsigned long addr, unsigned long end, pmdp = pmd_offset(&pud, addr); do { - pmd_t pmd = ACCESS_ONCE(*pmdp); + pmd_t pmd = READ_ONCE(*pmdp); next = pmd_addr_end(addr, end); if (pmd_none(pmd) || pmd_trans_splitting(pmd)) From 2a806d27573f9a2df193e5a374fe8e452be9c6c7 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Tue, 6 Jan 2015 22:47:41 +0100 Subject: [PATCH 179/788] ppc/hugetlbfs: Replace ACCESS_ONCE with READ_ONCE commit da1a288d8562739aa8ba0273d4fb6b73b856c0d3 upstream. ACCESS_ONCE does not work reliably on non-scalar types. For example gcc 4.6 and 4.7 might remove the volatile tag for such accesses during the SRA (scalar replacement of aggregates) step (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58145) Change the ppc/hugetlbfs code to replace ACCESS_ONCE with READ_ONCE. Signed-off-by: Christian Borntraeger Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/mm/hugetlbpage.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c index 5ff4e07d920a36..620d0ec93e6f98 100644 --- a/arch/powerpc/mm/hugetlbpage.c +++ b/arch/powerpc/mm/hugetlbpage.c @@ -978,7 +978,7 @@ pte_t *find_linux_pte_or_hugepte(pgd_t *pgdir, unsigned long ea, unsigned *shift */ pdshift = PUD_SHIFT; pudp = pud_offset(&pgd, ea); - pud = ACCESS_ONCE(*pudp); + pud = READ_ONCE(*pudp); if (pud_none(pud)) return NULL; @@ -990,7 +990,7 @@ pte_t *find_linux_pte_or_hugepte(pgd_t *pgdir, unsigned long ea, unsigned *shift else { pdshift = PMD_SHIFT; pmdp = pmd_offset(&pud, ea); - pmd = ACCESS_ONCE(*pmdp); + pmd = READ_ONCE(*pmdp); /* * A hugepage collapse is captured by pmd_none, because * it mark the pmd none and do a hpte invalidate. From 3c201105568388046da448947e8abc8673fedfd9 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Tue, 6 Jan 2015 22:41:46 +0100 Subject: [PATCH 180/788] ppc/kvm: Replace ACCESS_ONCE with READ_ONCE commit 5ee07612e9e20817bb99256ab6cf1400fd5aa270 upstream. ACCESS_ONCE does not work reliably on non-scalar types. For example gcc 4.6 and 4.7 might remove the volatile tag for such accesses during the SRA (scalar replacement of aggregates) step (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58145) Change the ppc/kvm code to replace ACCESS_ONCE with READ_ONCE. Signed-off-by: Christian Borntraeger Acked-by: Alexander Graf Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/kvm/book3s_hv_rm_xics.c | 8 ++++---- arch/powerpc/kvm/book3s_xics.c | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/arch/powerpc/kvm/book3s_hv_rm_xics.c b/arch/powerpc/kvm/book3s_hv_rm_xics.c index 7b066f6b02aded..7c22997de90641 100644 --- a/arch/powerpc/kvm/book3s_hv_rm_xics.c +++ b/arch/powerpc/kvm/book3s_hv_rm_xics.c @@ -152,7 +152,7 @@ static void icp_rm_down_cppr(struct kvmppc_xics *xics, struct kvmppc_icp *icp, * in virtual mode. */ do { - old_state = new_state = ACCESS_ONCE(icp->state); + old_state = new_state = READ_ONCE(icp->state); /* Down_CPPR */ new_state.cppr = new_cppr; @@ -211,7 +211,7 @@ unsigned long kvmppc_rm_h_xirr(struct kvm_vcpu *vcpu) * pending priority */ do { - old_state = new_state = ACCESS_ONCE(icp->state); + old_state = new_state = READ_ONCE(icp->state); xirr = old_state.xisr | (((u32)old_state.cppr) << 24); if (!old_state.xisr) @@ -277,7 +277,7 @@ int kvmppc_rm_h_ipi(struct kvm_vcpu *vcpu, unsigned long server, * whenever the MFRR is made less favored. */ do { - old_state = new_state = ACCESS_ONCE(icp->state); + old_state = new_state = READ_ONCE(icp->state); /* Set_MFRR */ new_state.mfrr = mfrr; @@ -352,7 +352,7 @@ int kvmppc_rm_h_cppr(struct kvm_vcpu *vcpu, unsigned long cppr) icp_rm_clr_vcpu_irq(icp->vcpu); do { - old_state = new_state = ACCESS_ONCE(icp->state); + old_state = new_state = READ_ONCE(icp->state); reject = 0; new_state.cppr = cppr; diff --git a/arch/powerpc/kvm/book3s_xics.c b/arch/powerpc/kvm/book3s_xics.c index 807351f76f84e8..a4a8d9f0dcb735 100644 --- a/arch/powerpc/kvm/book3s_xics.c +++ b/arch/powerpc/kvm/book3s_xics.c @@ -327,7 +327,7 @@ static bool icp_try_to_deliver(struct kvmppc_icp *icp, u32 irq, u8 priority, icp->server_num); do { - old_state = new_state = ACCESS_ONCE(icp->state); + old_state = new_state = READ_ONCE(icp->state); *reject = 0; @@ -512,7 +512,7 @@ static void icp_down_cppr(struct kvmppc_xics *xics, struct kvmppc_icp *icp, * in virtual mode. */ do { - old_state = new_state = ACCESS_ONCE(icp->state); + old_state = new_state = READ_ONCE(icp->state); /* Down_CPPR */ new_state.cppr = new_cppr; @@ -567,7 +567,7 @@ static noinline unsigned long kvmppc_h_xirr(struct kvm_vcpu *vcpu) * pending priority */ do { - old_state = new_state = ACCESS_ONCE(icp->state); + old_state = new_state = READ_ONCE(icp->state); xirr = old_state.xisr | (((u32)old_state.cppr) << 24); if (!old_state.xisr) @@ -634,7 +634,7 @@ static noinline int kvmppc_h_ipi(struct kvm_vcpu *vcpu, unsigned long server, * whenever the MFRR is made less favored. */ do { - old_state = new_state = ACCESS_ONCE(icp->state); + old_state = new_state = READ_ONCE(icp->state); /* Set_MFRR */ new_state.mfrr = mfrr; @@ -679,7 +679,7 @@ static int kvmppc_h_ipoll(struct kvm_vcpu *vcpu, unsigned long server) if (!icp) return H_PARAMETER; } - state = ACCESS_ONCE(icp->state); + state = READ_ONCE(icp->state); kvmppc_set_gpr(vcpu, 4, ((u32)state.cppr << 24) | state.xisr); kvmppc_set_gpr(vcpu, 5, state.mfrr); return H_SUCCESS; @@ -721,7 +721,7 @@ static noinline void kvmppc_h_cppr(struct kvm_vcpu *vcpu, unsigned long cppr) BOOK3S_INTERRUPT_EXTERNAL_LEVEL); do { - old_state = new_state = ACCESS_ONCE(icp->state); + old_state = new_state = READ_ONCE(icp->state); reject = 0; new_state.cppr = cppr; @@ -885,7 +885,7 @@ static int xics_debug_show(struct seq_file *m, void *private) if (!icp) continue; - state.raw = ACCESS_ONCE(icp->state.raw); + state.raw = READ_ONCE(icp->state.raw); seq_printf(m, "cpu server %#lx XIRR:%#x PPRI:%#x CPPR:%#x MFRR:%#x OUT:%d NR:%d\n", icp->server_num, state.xisr, state.pending_pri, state.cppr, state.mfrr, @@ -1082,7 +1082,7 @@ int kvmppc_xics_set_icp(struct kvm_vcpu *vcpu, u64 icpval) * the ICS states before the ICP states. */ do { - old_state = ACCESS_ONCE(icp->state); + old_state = READ_ONCE(icp->state); if (new_state.mfrr <= old_state.mfrr) { resend = false; From 5392bc6bce5ff16ca78d7d3780bde272f9119bb8 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 6 Mar 2015 14:57:59 -0800 Subject: [PATCH 181/788] Linux 3.19.1 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b15036b1890cae..688777b1786995 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 19 -SUBLEVEL = 0 +SUBLEVEL = 1 EXTRAVERSION = NAME = Diseased Newt From 3ec37c5e28b90c8f9f5173c0a7a61d513684f7b5 Mon Sep 17 00:00:00 2001 From: Miroslav Urbanek Date: Thu, 5 Feb 2015 16:36:50 +0100 Subject: [PATCH 182/788] flowcache: Fix kernel panic in flow_cache_flush_task MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 233c96fc077d310772375d47522fb444ff546905 ] flow_cache_flush_task references a structure member flow_cache_gc_work where it should reference flow_cache_flush_task instead. Kernel panic occurs on kernels using IPsec during XFRM garbage collection. The garbage collection interval can be shortened using the following sysctl settings: net.ipv4.xfrm4_gc_thresh=4 net.ipv6.xfrm6_gc_thresh=4 With the default settings, our productions servers crash approximately once a week. With the settings above, they crash immediately. Fixes: ca925cf1534e ("flowcache: Make flow cache name space aware") Reported-by: Tomáš Charvát Tested-by: Jan Hejl Signed-off-by: Miroslav Urbanek Acked-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/core/flow.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/core/flow.c b/net/core/flow.c index a0348fde1fdfe8..1033725be40bd8 100644 --- a/net/core/flow.c +++ b/net/core/flow.c @@ -379,7 +379,7 @@ void flow_cache_flush(struct net *net) static void flow_cache_flush_task(struct work_struct *work) { struct netns_xfrm *xfrm = container_of(work, struct netns_xfrm, - flow_cache_gc_work); + flow_cache_flush_work); struct net *net = container_of(xfrm, struct net, xfrm); flow_cache_flush(net); From 5484abd9b6a0449ccca3875dc3eee4ad7e6ebcdd Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Thu, 5 Feb 2015 14:39:11 +0100 Subject: [PATCH 183/788] ipv6: addrconf: add missing validate_link_af handler [ Upstream commit 11b1f8288d4341af5d755281c871bff6c3e270dd ] We still need a validate_link_af() handler with an appropriate nla policy, similarly as we have in IPv4 case, otherwise size validations are not being done properly in that case. Fixes: f53adae4eae5 ("net: ipv6: add tokenized interface identifier support") Fixes: bc91b0f07ada ("ipv6: addrconf: implement address generation modes") Cc: Jiri Pirko Signed-off-by: Daniel Borkmann Acked-by: Jiri Pirko Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv6/addrconf.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index f7c8bbeb27b704..dac94195a4b95e 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -4572,6 +4572,22 @@ static int inet6_set_iftoken(struct inet6_dev *idev, struct in6_addr *token) return 0; } +static const struct nla_policy inet6_af_policy[IFLA_INET6_MAX + 1] = { + [IFLA_INET6_ADDR_GEN_MODE] = { .type = NLA_U8 }, + [IFLA_INET6_TOKEN] = { .len = sizeof(struct in6_addr) }, +}; + +static int inet6_validate_link_af(const struct net_device *dev, + const struct nlattr *nla) +{ + struct nlattr *tb[IFLA_INET6_MAX + 1]; + + if (dev && !__in6_dev_get(dev)) + return -EAFNOSUPPORT; + + return nla_parse_nested(tb, IFLA_INET6_MAX, nla, inet6_af_policy); +} + static int inet6_set_link_af(struct net_device *dev, const struct nlattr *nla) { int err = -EINVAL; @@ -5393,6 +5409,7 @@ static struct rtnl_af_ops inet6_ops = { .family = AF_INET6, .fill_link_af = inet6_fill_link_af, .get_link_af_size = inet6_get_link_af_size, + .validate_link_af = inet6_validate_link_af, .set_link_af = inet6_set_link_af, }; From 18bb5d3023fbf4331128c67df627cd88ab47911b Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Wed, 4 Feb 2015 23:08:50 +0100 Subject: [PATCH 184/788] pktgen: fix UDP checksum computation [ Upstream commit 7744b5f3693cc06695cb9d6667671c790282730f ] This patch fixes two issues in UDP checksum computation in pktgen. First, the pseudo-header uses the source and destination IP addresses. Currently, the ports are used for IPv4. Second, the UDP checksum covers both header and data. So we need to generate the data earlier (move pktgen_finalize_skb up), and compute the checksum for UDP header + data. Fixes: c26bf4a51308c ("pktgen: Add UDPCSUM flag to support UDP checksums") Signed-off-by: Sabrina Dubroca Acked-by: Thomas Graf Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/core/pktgen.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/net/core/pktgen.c b/net/core/pktgen.c index da934fc3faa824..9fa25b0ea1450f 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -2842,25 +2842,25 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev, skb->dev = odev; skb->pkt_type = PACKET_HOST; + pktgen_finalize_skb(pkt_dev, skb, datalen); + if (!(pkt_dev->flags & F_UDPCSUM)) { skb->ip_summed = CHECKSUM_NONE; } else if (odev->features & NETIF_F_V4_CSUM) { skb->ip_summed = CHECKSUM_PARTIAL; skb->csum = 0; - udp4_hwcsum(skb, udph->source, udph->dest); + udp4_hwcsum(skb, iph->saddr, iph->daddr); } else { - __wsum csum = udp_csum(skb); + __wsum csum = skb_checksum(skb, skb_transport_offset(skb), datalen + 8, 0); /* add protocol-dependent pseudo-header */ - udph->check = csum_tcpudp_magic(udph->source, udph->dest, + udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr, datalen + 8, IPPROTO_UDP, csum); if (udph->check == 0) udph->check = CSUM_MANGLED_0; } - pktgen_finalize_skb(pkt_dev, skb, datalen); - #ifdef CONFIG_XFRM if (!process_ipsec(pkt_dev, skb, protocol)) return NULL; @@ -2976,6 +2976,8 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev, skb->dev = odev; skb->pkt_type = PACKET_HOST; + pktgen_finalize_skb(pkt_dev, skb, datalen); + if (!(pkt_dev->flags & F_UDPCSUM)) { skb->ip_summed = CHECKSUM_NONE; } else if (odev->features & NETIF_F_V6_CSUM) { @@ -2984,7 +2986,7 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev, skb->csum_offset = offsetof(struct udphdr, check); udph->check = ~csum_ipv6_magic(&iph->saddr, &iph->daddr, udplen, IPPROTO_UDP, 0); } else { - __wsum csum = udp_csum(skb); + __wsum csum = skb_checksum(skb, skb_transport_offset(skb), udplen, 0); /* add protocol-dependent pseudo-header */ udph->check = csum_ipv6_magic(&iph->saddr, &iph->daddr, udplen, IPPROTO_UDP, csum); @@ -2993,8 +2995,6 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev, udph->check = CSUM_MANGLED_0; } - pktgen_finalize_skb(pkt_dev, skb, datalen); - return skb; } From 4f630d422d9a18523c75be7fa81d413210904a44 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Thu, 5 Feb 2015 18:44:04 +0100 Subject: [PATCH 185/788] rtnetlink: ifla_vf_policy: fix misuses of NLA_BINARY [ Upstream commit 364d5716a7adb91b731a35765d369602d68d2881 ] ifla_vf_policy[] is wrong in advertising its individual member types as NLA_BINARY since .type = NLA_BINARY in combination with .len declares the len member as *max* attribute length [0, len]. The issue is that when do_setvfinfo() is being called to set up a VF through ndo handler, we could set corrupted data if the attribute length is less than the size of the related structure itself. The intent is exactly the opposite, namely to make sure to pass at least data of minimum size of len. Fixes: ebc08a6f47ee ("rtnetlink: Add VF config code to rtnetlink") Cc: Mitch Williams Cc: Jeff Kirsher Signed-off-by: Daniel Borkmann Acked-by: Thomas Graf Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/core/rtnetlink.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 446cbaf8118571..5daabfda6f6fab 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1237,18 +1237,12 @@ static const struct nla_policy ifla_vfinfo_policy[IFLA_VF_INFO_MAX+1] = { }; static const struct nla_policy ifla_vf_policy[IFLA_VF_MAX+1] = { - [IFLA_VF_MAC] = { .type = NLA_BINARY, - .len = sizeof(struct ifla_vf_mac) }, - [IFLA_VF_VLAN] = { .type = NLA_BINARY, - .len = sizeof(struct ifla_vf_vlan) }, - [IFLA_VF_TX_RATE] = { .type = NLA_BINARY, - .len = sizeof(struct ifla_vf_tx_rate) }, - [IFLA_VF_SPOOFCHK] = { .type = NLA_BINARY, - .len = sizeof(struct ifla_vf_spoofchk) }, - [IFLA_VF_RATE] = { .type = NLA_BINARY, - .len = sizeof(struct ifla_vf_rate) }, - [IFLA_VF_LINK_STATE] = { .type = NLA_BINARY, - .len = sizeof(struct ifla_vf_link_state) }, + [IFLA_VF_MAC] = { .len = sizeof(struct ifla_vf_mac) }, + [IFLA_VF_VLAN] = { .len = sizeof(struct ifla_vf_vlan) }, + [IFLA_VF_TX_RATE] = { .len = sizeof(struct ifla_vf_tx_rate) }, + [IFLA_VF_SPOOFCHK] = { .len = sizeof(struct ifla_vf_spoofchk) }, + [IFLA_VF_RATE] = { .len = sizeof(struct ifla_vf_rate) }, + [IFLA_VF_LINK_STATE] = { .len = sizeof(struct ifla_vf_link_state) }, }; static const struct nla_policy ifla_port_policy[IFLA_PORT_MAX+1] = { From aee614696c22c6ba6a12de0382e814a347ef25e0 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Mon, 9 Feb 2015 09:38:20 -0500 Subject: [PATCH 186/788] ipv6: Fix fragment id assignment on LE arches. [ Upstream commit 51f30770e50eb787200f30a79105e2615b379334 ] Recent commit: 0508c07f5e0c94f38afd5434e8b2a55b84553077 Author: Vlad Yasevich Date: Tue Feb 3 16:36:15 2015 -0500 ipv6: Select fragment id during UFO segmentation if not set. Introduced a bug on LE in how ipv6 fragment id is assigned. This was cought by nightly sparce check: Resolve the following sparce error: net/ipv6/output_core.c:57:38: sparse: incorrect type in assignment (different base types) net/ipv6/output_core.c:57:38: expected restricted __be32 [usertype] ip6_frag_id net/ipv6/output_core.c:57:38: got unsigned int [unsigned] [assigned] [usertype] id Fixes: 0508c07f5e0c9 (ipv6: Select fragment id during UFO segmentation if not set.) Signed-off-by: Vladislav Yasevich Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv6/output_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c index 54520a0bd5e3b5..a86cf60f0ca61d 100644 --- a/net/ipv6/output_core.c +++ b/net/ipv6/output_core.c @@ -54,7 +54,7 @@ void ipv6_proxy_select_ident(struct sk_buff *skb) id = __ipv6_select_ident(ip6_proxy_idents_hashrnd, &addrs[1], &addrs[0]); - skb_shinfo(skb)->ip6_frag_id = id; + skb_shinfo(skb)->ip6_frag_id = htonl(id); } EXPORT_SYMBOL_GPL(ipv6_proxy_select_ident); From 9519ba7401b98c4c882d06b218d14dce45fcb7c1 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Mon, 9 Feb 2015 09:38:21 -0500 Subject: [PATCH 187/788] ipv6: Make __ipv6_select_ident static [ Upstream commit 8381eacf5c3b35cf7755f4bc521c4d56d24c1cd9 ] Make __ipv6_select_ident() static as it isn't used outside the file. Fixes: 0508c07f5e0c9 (ipv6: Select fragment id during UFO segmentation if not set.) Signed-off-by: Vladislav Yasevich Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- include/net/ipv6.h | 2 -- net/ipv6/output_core.c | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 6e416f6d3e3cbe..fde3b593c3f242 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -671,8 +671,6 @@ static inline int ipv6_addr_diff(const struct in6_addr *a1, const struct in6_add return __ipv6_addr_diff(a1, a2, sizeof(struct in6_addr)); } -u32 __ipv6_select_ident(u32 hashrnd, struct in6_addr *dst, - struct in6_addr *src); void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt); void ipv6_proxy_select_ident(struct sk_buff *skb); diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c index a86cf60f0ca61d..74581f706c4da4 100644 --- a/net/ipv6/output_core.c +++ b/net/ipv6/output_core.c @@ -9,7 +9,8 @@ #include #include -u32 __ipv6_select_ident(u32 hashrnd, struct in6_addr *dst, struct in6_addr *src) +static u32 __ipv6_select_ident(u32 hashrnd, struct in6_addr *dst, + struct in6_addr *src) { u32 hash, id; From b73f6e471cdd6c4474aff6b6301af2779462d98d Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 13 Feb 2015 04:47:12 -0800 Subject: [PATCH 188/788] tcp: make sure skb is not shared before using skb_get() [ Upstream commit ba34e6d9d346fe4e05d7e417b9edf5140772d34c ] IPv6 can keep a copy of SYN message using skb_get() in tcp_v6_conn_request() so that caller wont free the skb when calling kfree_skb() later. Therefore TCP fast open has to clone the skb it is queuing in child->sk_receive_queue, as all skbs consumed from receive_queue are freed using __kfree_skb() (ie assuming skb->users == 1) Signed-off-by: Eric Dumazet Signed-off-by: Yuchung Cheng Fixes: 5b7ed0892f2af ("tcp: move fastopen functions to tcp_fastopen.c") Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/tcp_fastopen.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c index 815c85e3b1e064..c73077280ad473 100644 --- a/net/ipv4/tcp_fastopen.c +++ b/net/ipv4/tcp_fastopen.c @@ -134,6 +134,7 @@ static bool tcp_fastopen_create_child(struct sock *sk, struct tcp_sock *tp; struct request_sock_queue *queue = &inet_csk(sk)->icsk_accept_queue; struct sock *child; + u32 end_seq; req->num_retrans = 0; req->num_timeout = 0; @@ -185,20 +186,35 @@ static bool tcp_fastopen_create_child(struct sock *sk, /* Queue the data carried in the SYN packet. We need to first * bump skb's refcnt because the caller will attempt to free it. + * Note that IPv6 might also have used skb_get() trick + * in tcp_v6_conn_request() to keep this SYN around (treq->pktopts) + * So we need to eventually get a clone of the packet, + * before inserting it in sk_receive_queue. * * XXX (TFO) - we honor a zero-payload TFO request for now, * (any reason not to?) but no need to queue the skb since * there is no data. How about SYN+FIN? */ - if (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq + 1) { - skb = skb_get(skb); - skb_dst_drop(skb); - __skb_pull(skb, tcp_hdr(skb)->doff * 4); - skb_set_owner_r(skb, child); - __skb_queue_tail(&child->sk_receive_queue, skb); - tp->syn_data_acked = 1; + end_seq = TCP_SKB_CB(skb)->end_seq; + if (end_seq != TCP_SKB_CB(skb)->seq + 1) { + struct sk_buff *skb2; + + if (unlikely(skb_shared(skb))) + skb2 = skb_clone(skb, GFP_ATOMIC); + else + skb2 = skb_get(skb); + + if (likely(skb2)) { + skb_dst_drop(skb2); + __skb_pull(skb2, tcp_hdrlen(skb)); + skb_set_owner_r(skb2, child); + __skb_queue_tail(&child->sk_receive_queue, skb2); + tp->syn_data_acked = 1; + } else { + end_seq = TCP_SKB_CB(skb)->seq + 1; + } } - tcp_rsk(req)->rcv_nxt = tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq; + tcp_rsk(req)->rcv_nxt = tp->rcv_nxt = end_seq; sk->sk_data_ready(sk); bh_unlock_sock(child); sock_put(child); From 29ec76704d111f747e5640ce3998611c5407049b Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Thu, 12 Feb 2015 16:14:08 -0800 Subject: [PATCH 189/788] ipv6: fix ipv6_cow_metrics for non DST_HOST case [ Upstream commit 3b4711757d7903ab6fa88a9e7ab8901b8227da60 ] ipv6_cow_metrics() currently assumes only DST_HOST routes require dynamic metrics allocation from inetpeer. The assumption breaks when ndisc discovered router with RTAX_MTU and RTAX_HOPLIMIT metric. Refer to ndisc_router_discovery() in ndisc.c and note that dst_metric_set() is called after the route is created. This patch creates the metrics array (by calling dst_cow_metrics_generic) in ipv6_cow_metrics(). Test: radvd.conf: interface qemubr0 { AdvLinkMTU 1300; AdvCurHopLimit 30; prefix fd00:face:face:face::/64 { AdvOnLink on; AdvAutonomous on; AdvRouterAddr off; }; }; Before: [root@qemu1 ~]# ip -6 r show | egrep -v unreachable fd00:face:face:face::/64 dev eth0 proto kernel metric 256 expires 27sec fe80::/64 dev eth0 proto kernel metric 256 default via fe80::74df:d0ff:fe23:8ef2 dev eth0 proto ra metric 1024 expires 27sec After: [root@qemu1 ~]# ip -6 r show | egrep -v unreachable fd00:face:face:face::/64 dev eth0 proto kernel metric 256 expires 27sec mtu 1300 fe80::/64 dev eth0 proto kernel metric 256 mtu 1300 default via fe80::74df:d0ff:fe23:8ef2 dev eth0 proto ra metric 1024 expires 27sec mtu 1300 hoplimit 30 Fixes: 8e2ec639173f325 (ipv6: don't use inetpeer to store metrics for routes.) Signed-off-by: Martin KaFai Lau Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv6/route.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 495965358d22d8..1528d8404cd20b 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -141,7 +141,7 @@ static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old) u32 *p = NULL; if (!(rt->dst.flags & DST_HOST)) - return NULL; + return dst_cow_metrics_generic(dst, old); peer = rt6_get_peer_create(rt); if (peer) { From 2f3b6173e44a8add131deffe49c556472301f281 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Fri, 13 Feb 2015 13:56:53 -0800 Subject: [PATCH 190/788] rtnetlink: call ->dellink on failure when ->newlink exists [ Upstream commit 7afb8886a05be68e376655539a064ec672de8a8e ] Ignacy reported that when eth0 is down and add a vlan device on top of it like: ip link add link eth0 name eth0.1 up type vlan id 1 We will get a refcount leak: unregister_netdevice: waiting for eth0.1 to become free. Usage count = 2 The problem is when rtnl_configure_link() fails in rtnl_newlink(), we simply call unregister_device(), but for stacked device like vlan, we almost do nothing when we unregister the upper device, more work is done when we unregister the lower device, so call its ->dellink(). Reported-by: Ignacy Gawedzki Signed-off-by: Cong Wang Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/core/rtnetlink.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 5daabfda6f6fab..d582e67a6ee01c 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2124,8 +2124,16 @@ static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh) } } err = rtnl_configure_link(dev, ifm); - if (err < 0) - unregister_netdevice(dev); + if (err < 0) { + if (ops->newlink) { + LIST_HEAD(list_kill); + + ops->dellink(dev, &list_kill); + unregister_netdevice_many(&list_kill); + } else { + unregister_netdevice(dev); + } + } out: put_net(dest_net); return err; From e67e4522676c60772d3bf476bfbf99fd7d4c1aca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacy=20Gaw=C4=99dzki?= Date: Fri, 13 Feb 2015 14:47:05 -0800 Subject: [PATCH 191/788] gen_stats.c: Duplicate xstats buffer for later use MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 1c4cff0cf55011792125b6041bc4e9713e46240f ] The gnet_stats_copy_app() function gets called, more often than not, with its second argument a pointer to an automatic variable in the caller's stack. Therefore, to avoid copying garbage afterwards when calling gnet_stats_finish_copy(), this data is better copied to a dynamically allocated memory that gets freed after use. [xiyou.wangcong@gmail.com: remove a useless kfree()] Signed-off-by: Ignacy Gawędzki Signed-off-by: Cong Wang Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/core/gen_stats.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/net/core/gen_stats.c b/net/core/gen_stats.c index 0c08062d179633..1e2f46a69d5019 100644 --- a/net/core/gen_stats.c +++ b/net/core/gen_stats.c @@ -32,6 +32,9 @@ gnet_stats_copy(struct gnet_dump *d, int type, void *buf, int size) return 0; nla_put_failure: + kfree(d->xstats); + d->xstats = NULL; + d->xstats_len = 0; spin_unlock_bh(d->lock); return -1; } @@ -305,7 +308,9 @@ int gnet_stats_copy_app(struct gnet_dump *d, void *st, int len) { if (d->compat_xstats) { - d->xstats = st; + d->xstats = kmemdup(st, len, GFP_ATOMIC); + if (!d->xstats) + goto err_out; d->xstats_len = len; } @@ -313,6 +318,11 @@ gnet_stats_copy_app(struct gnet_dump *d, void *st, int len) return gnet_stats_copy(d, TCA_STATS_APP, st, len); return 0; + +err_out: + d->xstats_len = 0; + spin_unlock_bh(d->lock); + return -1; } EXPORT_SYMBOL(gnet_stats_copy_app); @@ -345,6 +355,9 @@ gnet_stats_finish_copy(struct gnet_dump *d) return -1; } + kfree(d->xstats); + d->xstats = NULL; + d->xstats_len = 0; spin_unlock_bh(d->lock); return 0; } From 1ccd26c9825ebeb625f04d2e7ebe9c8478a20afe Mon Sep 17 00:00:00 2001 From: Alexander Drozdov Date: Tue, 17 Feb 2015 13:33:46 +0300 Subject: [PATCH 192/788] ipv4: ip_check_defrag should correctly check return value of skb_copy_bits [ Upstream commit fba04a9e0c869498889b6445fd06cbe7da9bb834 ] skb_copy_bits() returns zero on success and negative value on error, so it is needed to invert the condition in ip_check_defrag(). Fixes: 1bf3751ec90c ("ipv4: ip_check_defrag must not modify skb before unsharing") Signed-off-by: Alexander Drozdov Acked-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/ip_fragment.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index e5b6d0ddcb5808..2c8d98e728c09f 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -664,7 +664,7 @@ struct sk_buff *ip_check_defrag(struct sk_buff *skb, u32 user) if (skb->protocol != htons(ETH_P_IP)) return skb; - if (!skb_copy_bits(skb, 0, &iph, sizeof(iph))) + if (skb_copy_bits(skb, 0, &iph, sizeof(iph)) < 0) return skb; if (iph.ihl < 5 || iph.version != 4) From 8959499caafe926472ef0475344a913c825c2708 Mon Sep 17 00:00:00 2001 From: Alexander Drozdov Date: Thu, 5 Mar 2015 10:29:39 +0300 Subject: [PATCH 193/788] ipv4: ip_check_defrag should not assume that skb_network_offset is zero [ Upstream commit 3e32e733d1bbb3f227259dc782ef01d5706bdae0 ] ip_check_defrag() may be used by af_packet to defragment outgoing packets. skb_network_offset() of af_packet's outgoing packets is not zero. Signed-off-by: Alexander Drozdov Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/ip_fragment.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 2c8d98e728c09f..145a50c4d56630 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -659,27 +659,30 @@ EXPORT_SYMBOL(ip_defrag); struct sk_buff *ip_check_defrag(struct sk_buff *skb, u32 user) { struct iphdr iph; + int netoff; u32 len; if (skb->protocol != htons(ETH_P_IP)) return skb; - if (skb_copy_bits(skb, 0, &iph, sizeof(iph)) < 0) + netoff = skb_network_offset(skb); + + if (skb_copy_bits(skb, netoff, &iph, sizeof(iph)) < 0) return skb; if (iph.ihl < 5 || iph.version != 4) return skb; len = ntohs(iph.tot_len); - if (skb->len < len || len < (iph.ihl * 4)) + if (skb->len < netoff + len || len < (iph.ihl * 4)) return skb; if (ip_is_fragment(&iph)) { skb = skb_share_check(skb, GFP_ATOMIC); if (skb) { - if (!pskb_may_pull(skb, iph.ihl*4)) + if (!pskb_may_pull(skb, netoff + iph.ihl * 4)) return skb; - if (pskb_trim_rcsum(skb, len)) + if (pskb_trim_rcsum(skb, netoff + len)) return skb; memset(IPCB(skb), 0, sizeof(struct inet_skb_parm)); if (ip_defrag(skb, user)) From 1dd8e3243c434a4f45bf2a5d1d3a9a0baf37ef64 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 17 Feb 2015 09:36:22 -0800 Subject: [PATCH 194/788] net: phy: Fix verification of EEE support in phy_init_eee [ Upstream commit 54da5a8be3c1e924c35480eb44c6e9b275f6444e ] phy_init_eee uses phy_find_setting(phydev->speed, phydev->duplex) to find a valid entry in the settings array for the given speed and duplex value. For full duplex 1000baseT, this will return the first matching entry, which is the entry for 1000baseKX_Full. If the phy eee does not support 1000baseKX_Full, this entry will not match, causing phy_init_eee to fail for no good reason. Fixes: 9a9c56cb34e6 ("net: phy: fix a bug when verify the EEE support") Fixes: 3e7077067e80c ("phy: Expand phy speed/duplex settings array") Cc: Giuseppe Cavallaro Signed-off-by: Guenter Roeck Acked-by: Florian Fainelli Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/phy/phy.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 767cd110f49688..dc1f6f07326ae6 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -235,6 +235,25 @@ static inline unsigned int phy_find_valid(unsigned int idx, u32 features) return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1; } +/** + * phy_check_valid - check if there is a valid PHY setting which matches + * speed, duplex, and feature mask + * @speed: speed to match + * @duplex: duplex to match + * @features: A mask of the valid settings + * + * Description: Returns true if there is a valid setting, false otherwise. + */ +static inline bool phy_check_valid(int speed, int duplex, u32 features) +{ + unsigned int idx; + + idx = phy_find_valid(phy_find_setting(speed, duplex), features); + + return settings[idx].speed == speed && settings[idx].duplex == duplex && + (settings[idx].setting & features); +} + /** * phy_sanitize_settings - make sure the PHY is set to supported speed and duplex * @phydev: the target phy_device struct @@ -1042,7 +1061,6 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) int eee_lp, eee_cap, eee_adv; u32 lp, cap, adv; int status; - unsigned int idx; /* Read phy status to properly get the right settings */ status = phy_read_status(phydev); @@ -1074,8 +1092,7 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) adv = mmd_eee_adv_to_ethtool_adv_t(eee_adv); lp = mmd_eee_adv_to_ethtool_adv_t(eee_lp); - idx = phy_find_setting(phydev->speed, phydev->duplex); - if (!(lp & adv & settings[idx].setting)) + if (!phy_check_valid(phydev->speed, phydev->duplex, lp & adv)) goto eee_exit_err; if (clk_stop_enable) { From 7f77f6c882bcd27a9bf750f96e9fe63ecbe443f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacy=20Gaw=C4=99dzki?= Date: Tue, 17 Feb 2015 20:15:20 +0100 Subject: [PATCH 195/788] ematch: Fix auto-loading of ematch modules. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 34eea79e2664b314cab6a30fc582fdfa7a1bb1df ] In tcf_em_validate(), after calling request_module() to load the kind-specific module, set em->ops to NULL before returning -EAGAIN, so that module_put() is not called again by tcf_em_tree_destroy(). Signed-off-by: Ignacy Gawędzki Acked-by: Cong Wang Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/sched/ematch.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/sched/ematch.c b/net/sched/ematch.c index 6742200b13071b..fbb7ebfc58c676 100644 --- a/net/sched/ematch.c +++ b/net/sched/ematch.c @@ -228,6 +228,7 @@ static int tcf_em_validate(struct tcf_proto *tp, * to replay the request. */ module_put(em->ops->owner); + em->ops = NULL; err = -EAGAIN; } #endif From 488da593c9e4eafd5783a631e644af67fd6cf003 Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Tue, 17 Feb 2015 11:23:10 -0800 Subject: [PATCH 196/788] openvswitch: Fix net exit. [ Upstream commit 7b4577a9da3702049650f7095506e9afd9f68849 ] Open vSwitch allows moving internal vport to different namespace while still connected to the bridge. But when namespace deleted OVS does not detach these vports, that results in dangling pointer to netdevice which causes kernel panic as follows. This issue is fixed by detaching all ovs ports from the deleted namespace at net-exit. BUG: unable to handle kernel NULL pointer dereference at 0000000000000028 IP: [] ovs_vport_locate+0x35/0x80 [openvswitch] Oops: 0000 [#1] SMP Call Trace: [] lookup_vport+0x21/0xd0 [openvswitch] [] ovs_vport_cmd_get+0x59/0xf0 [openvswitch] [] genl_family_rcv_msg+0x1bc/0x3e0 [] genl_rcv_msg+0x79/0xc0 [] netlink_rcv_skb+0xb9/0xe0 [] genl_rcv+0x2c/0x40 [] netlink_unicast+0x12d/0x1c0 [] netlink_sendmsg+0x34a/0x6b0 [] sock_sendmsg+0xa0/0xe0 [] ___sys_sendmsg+0x408/0x420 [] __sys_sendmsg+0x51/0x90 [] SyS_sendmsg+0x12/0x20 [] system_call_fastpath+0x12/0x17 Reported-by: Assaf Muller Fixes: 46df7b81454("openvswitch: Add support for network namespaces.") Signed-off-by: Pravin B Shelar Reviewed-by: Thomas Graf Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/openvswitch/datapath.c | 45 ++++++++++++++++++++++++++++++++++++-- net/openvswitch/vport.h | 2 ++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index b07349e82d788d..58a6ef58c01793 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -2113,14 +2113,55 @@ static int __net_init ovs_init_net(struct net *net) return 0; } -static void __net_exit ovs_exit_net(struct net *net) +static void __net_exit list_vports_from_net(struct net *net, struct net *dnet, + struct list_head *head) { - struct datapath *dp, *dp_next; struct ovs_net *ovs_net = net_generic(net, ovs_net_id); + struct datapath *dp; + + list_for_each_entry(dp, &ovs_net->dps, list_node) { + int i; + + for (i = 0; i < DP_VPORT_HASH_BUCKETS; i++) { + struct vport *vport; + + hlist_for_each_entry(vport, &dp->ports[i], dp_hash_node) { + struct netdev_vport *netdev_vport; + + if (vport->ops->type != OVS_VPORT_TYPE_INTERNAL) + continue; + + netdev_vport = netdev_vport_priv(vport); + if (dev_net(netdev_vport->dev) == dnet) + list_add(&vport->detach_list, head); + } + } + } +} + +static void __net_exit ovs_exit_net(struct net *dnet) +{ + struct datapath *dp, *dp_next; + struct ovs_net *ovs_net = net_generic(dnet, ovs_net_id); + struct vport *vport, *vport_next; + struct net *net; + LIST_HEAD(head); ovs_lock(); list_for_each_entry_safe(dp, dp_next, &ovs_net->dps, list_node) __dp_destroy(dp); + + rtnl_lock(); + for_each_net(net) + list_vports_from_net(net, dnet, &head); + rtnl_unlock(); + + /* Detach all vports from given namespace. */ + list_for_each_entry_safe(vport, vport_next, &head, detach_list) { + list_del(&vport->detach_list); + ovs_dp_detach_port(vport); + } + ovs_unlock(); cancel_work_sync(&ovs_net->dp_notify_work); diff --git a/net/openvswitch/vport.h b/net/openvswitch/vport.h index 99c8e71d9e6cb1..8a057d7a86b67e 100644 --- a/net/openvswitch/vport.h +++ b/net/openvswitch/vport.h @@ -103,6 +103,7 @@ struct vport_portids { * @ops: Class structure. * @percpu_stats: Points to per-CPU statistics used and maintained by vport * @err_stats: Points to error statistics used and maintained by vport + * @detach_list: list used for detaching vport in net-exit call. */ struct vport { struct rcu_head rcu; @@ -117,6 +118,7 @@ struct vport { struct pcpu_sw_netstats __percpu *percpu_stats; struct vport_err_stats err_stats; + struct list_head detach_list; }; /** From 9d0ba3cf285bc815de7d4cf3fc2bf9fe4d4e5c5c Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 18 Feb 2015 05:47:55 -0800 Subject: [PATCH 197/788] sock: sock_dequeue_err_skb() needs hard irq safety [ Upstream commit 997d5c3f4427f38562cbe207ce05bb25fdcb993b ] Non NAPI drivers can call skb_tstamp_tx() and then sock_queue_err_skb() from hard IRQ context. Therefore, sock_dequeue_err_skb() needs to block hard irq or corruptions or hangs can happen. Signed-off-by: Eric Dumazet Fixes: 364a9e93243d1 ("sock: deduplicate errqueue dequeue") Fixes: cb820f8e4b7f7 ("net: Provide a generic socket error queue delivery method for Tx time stamps.") Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/core/skbuff.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 395c15b8208725..62c67bebcaf5cb 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -3623,13 +3623,14 @@ struct sk_buff *sock_dequeue_err_skb(struct sock *sk) { struct sk_buff_head *q = &sk->sk_error_queue; struct sk_buff *skb, *skb_next; + unsigned long flags; int err = 0; - spin_lock_bh(&q->lock); + spin_lock_irqsave(&q->lock, flags); skb = __skb_dequeue(q); if (skb && (skb_next = skb_peek(q))) err = SKB_EXT_ERR(skb_next)->ee.ee_errno; - spin_unlock_bh(&q->lock); + spin_unlock_irqrestore(&q->lock, flags); sk->sk_err = err; if (err) From 465146d28c3173d31229df1cf53a4696360a399d Mon Sep 17 00:00:00 2001 From: Matthew Thode Date: Tue, 17 Feb 2015 18:31:57 -0600 Subject: [PATCH 198/788] net: reject creation of netdev names with colons [ Upstream commit a4176a9391868bfa87705bcd2e3b49e9b9dd2996 ] colons are used as a separator in netdev device lookup in dev_ioctl.c Specific functions are SIOCGIFTXQLEN SIOCETHTOOL SIOCSIFNAME Signed-off-by: Matthew Thode Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/core/dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/core/dev.c b/net/core/dev.c index 7fe82929f5094e..4ff46f8054d411 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -945,7 +945,7 @@ bool dev_valid_name(const char *name) return false; while (*name) { - if (*name == '/' || isspace(*name)) + if (*name == '/' || *name == ':' || isspace(*name)) return false; name++; } From 9ae56d8b65097d9f5b95e49663af5f6a479fe839 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 10 Mar 2015 18:47:33 -0400 Subject: [PATCH 199/788] Revert "r8169: add support for Byte Queue Limits" This reverts commit 1e918876853aa85435e0f17fd8b4a92dcfff53d6. Revert BQL support in r8169 driver as several regressions point to this commit and we cannot figure out the real cause yet. Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/realtek/r8169.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index 14a1c5cec3a59f..2e2cf80e713583 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -5067,8 +5067,6 @@ static void rtl_hw_reset(struct rtl8169_private *tp) RTL_W8(ChipCmd, CmdReset); rtl_udelay_loop_wait_low(tp, &rtl_chipcmd_cond, 100, 100); - - netdev_reset_queue(tp->dev); } static void rtl_request_uncached_firmware(struct rtl8169_private *tp) @@ -7089,8 +7087,6 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb, txd->opts2 = cpu_to_le32(opts[1]); - netdev_sent_queue(dev, skb->len); - skb_tx_timestamp(skb); /* Force memory writes to complete before releasing descriptor */ @@ -7192,7 +7188,6 @@ static void rtl8169_pcierr_interrupt(struct net_device *dev) static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp) { unsigned int dirty_tx, tx_left; - unsigned int bytes_compl = 0, pkts_compl = 0; dirty_tx = tp->dirty_tx; smp_rmb(); @@ -7216,8 +7211,10 @@ static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp) rtl8169_unmap_tx_skb(&tp->pci_dev->dev, tx_skb, tp->TxDescArray + entry); if (status & LastFrag) { - pkts_compl++; - bytes_compl += tx_skb->skb->len; + u64_stats_update_begin(&tp->tx_stats.syncp); + tp->tx_stats.packets++; + tp->tx_stats.bytes += tx_skb->skb->len; + u64_stats_update_end(&tp->tx_stats.syncp); dev_kfree_skb_any(tx_skb->skb); tx_skb->skb = NULL; } @@ -7226,13 +7223,6 @@ static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp) } if (tp->dirty_tx != dirty_tx) { - netdev_completed_queue(tp->dev, pkts_compl, bytes_compl); - - u64_stats_update_begin(&tp->tx_stats.syncp); - tp->tx_stats.packets += pkts_compl; - tp->tx_stats.bytes += bytes_compl; - u64_stats_update_end(&tp->tx_stats.syncp); - tp->dirty_tx = dirty_tx; /* Sync with rtl8169_start_xmit: * - publish dirty_tx ring index (write barrier) From 37c63bf5b692b8311722b3b64c9d7fe0022d43bf Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 22 Feb 2015 17:03:41 -0800 Subject: [PATCH 200/788] net: pktgen: disable xmit_clone on virtual devices [ Upstream commit 52d6c8c6ca125872459054daa70f2f1c698c8e75 ] Trying to use burst capability (aka xmit_more) on a virtual device like bonding is not supported. For example, skb might be queued multiple times on a qdisc, with various list corruptions. Fixes: 38b2cf2982dc ("net: pktgen: packet bursting via skb->xmit_more") Signed-off-by: Eric Dumazet Cc: Alexei Starovoitov Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/core/pktgen.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 9fa25b0ea1450f..352d183ecba347 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -1134,6 +1134,9 @@ static ssize_t pktgen_if_write(struct file *file, return len; i += len; + if ((value > 1) && + (!(pkt_dev->odev->priv_flags & IFF_TX_SKB_SHARING))) + return -ENOTSUPP; pkt_dev->burst = value < 1 ? 1 : value; sprintf(pg_result, "OK: burst=%d", pkt_dev->burst); return count; From 82b92857cb40358a86d092db31aa667c5d87af1c Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 23 Feb 2015 14:02:54 +0100 Subject: [PATCH 201/788] team: fix possible null pointer dereference in team_handle_frame [ Upstream commit 57e595631904c827cfa1a0f7bbd7cc9a49da5745 ] Currently following race is possible in team: CPU0 CPU1 team_port_del team_upper_dev_unlink priv_flags &= ~IFF_TEAM_PORT team_handle_frame team_port_get_rcu team_port_exists priv_flags & IFF_TEAM_PORT == 0 return NULL (instead of port got from rx_handler_data) netdev_rx_handler_unregister The thing is that the flag is removed before rx_handler is unregistered. If team_handle_frame is called in between, team_port_exists returns 0 and team_port_get_rcu will return NULL. So do not check the flag here. It is guaranteed by netdev_rx_handler_unregister that team_handle_frame will always see valid rx_handler_data pointer. Signed-off-by: Jiri Pirko Fixes: 3d249d4ca7d0 ("net: introduce ethernet teaming device") Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/team/team.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index f7ff493f1e73df..221ba86323d15b 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -42,9 +42,7 @@ static struct team_port *team_port_get_rcu(const struct net_device *dev) { - struct team_port *port = rcu_dereference(dev->rx_handler_data); - - return team_port_exists(dev) ? port : NULL; + return rcu_dereference(dev->rx_handler_data); } static struct team_port *team_port_get_rtnl(const struct net_device *dev) From c9a44034d0a6e2b27636a082dbc3efc89935bcd1 Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Mon, 23 Feb 2015 18:12:56 +0000 Subject: [PATCH 202/788] net: compat: Ignore MSG_CMSG_COMPAT in compat_sys_{send, recv}msg [ Upstream commit d720d8cec563ce4e4fa44a613d4f2dcb1caf2998 ] With commit a7526eb5d06b (net: Unbreak compat_sys_{send,recv}msg), the MSG_CMSG_COMPAT flag is blocked at the compat syscall entry points, changing the kernel compat behaviour from the one before the commit it was trying to fix (1be374a0518a, net: Block MSG_CMSG_COMPAT in send(m)msg and recv(m)msg). On 32-bit kernels (!CONFIG_COMPAT), MSG_CMSG_COMPAT is 0 and the native 32-bit sys_sendmsg() allows flag 0x80000000 to be set (it is ignored by the kernel). However, on a 64-bit kernel, the compat ABI is different with commit a7526eb5d06b. This patch changes the compat_sys_{send,recv}msg behaviour to the one prior to commit 1be374a0518a. The problem was found running 32-bit LTP (sendmsg01) binary on an arm64 kernel. Arguably, LTP should not pass 0xffffffff as flags to sendmsg() but the general rule is not to break user ABI (even when the user behaviour is not entirely sane). Fixes: a7526eb5d06b (net: Unbreak compat_sys_{send,recv}msg) Cc: Andy Lutomirski Cc: David S. Miller Signed-off-by: Catalin Marinas Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/compat.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/net/compat.c b/net/compat.c index 3236b4167a3210..94d3d5e978832c 100644 --- a/net/compat.c +++ b/net/compat.c @@ -711,24 +711,18 @@ static unsigned char nas[21] = { COMPAT_SYSCALL_DEFINE3(sendmsg, int, fd, struct compat_msghdr __user *, msg, unsigned int, flags) { - if (flags & MSG_CMSG_COMPAT) - return -EINVAL; return __sys_sendmsg(fd, (struct user_msghdr __user *)msg, flags | MSG_CMSG_COMPAT); } COMPAT_SYSCALL_DEFINE4(sendmmsg, int, fd, struct compat_mmsghdr __user *, mmsg, unsigned int, vlen, unsigned int, flags) { - if (flags & MSG_CMSG_COMPAT) - return -EINVAL; return __sys_sendmmsg(fd, (struct mmsghdr __user *)mmsg, vlen, flags | MSG_CMSG_COMPAT); } COMPAT_SYSCALL_DEFINE3(recvmsg, int, fd, struct compat_msghdr __user *, msg, unsigned int, flags) { - if (flags & MSG_CMSG_COMPAT) - return -EINVAL; return __sys_recvmsg(fd, (struct user_msghdr __user *)msg, flags | MSG_CMSG_COMPAT); } @@ -751,9 +745,6 @@ COMPAT_SYSCALL_DEFINE5(recvmmsg, int, fd, struct compat_mmsghdr __user *, mmsg, int datagrams; struct timespec ktspec; - if (flags & MSG_CMSG_COMPAT) - return -EINVAL; - if (timeout == NULL) return __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen, flags | MSG_CMSG_COMPAT, NULL); From 72e726749c2067cbbe7ad7ed1df2ad559aeafa6d Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 27 Feb 2015 18:35:35 -0800 Subject: [PATCH 203/788] macvtap: make sure neighbour code can push ethernet header [ Upstream commit 2f1d8b9e8afa5a833d96afcd23abcb8cdf8d83ab ] Brian reported crashes using IPv6 traffic with macvtap/veth combo. I tracked the crashes in neigh_hh_output() -> memcpy(skb->data - HH_DATA_MOD, hh->hh_data, HH_DATA_MOD); Neighbour code assumes headroom to push Ethernet header is at least 16 bytes. It appears macvtap has only 14 bytes available on arches where NET_IP_ALIGN is 0 (like x86) Effect is a corruption of 2 bytes right before skb->head, and possible crashes if accessing non existing memory. This fix should also increase IPv4 performance, as paranoid code in ip_finish_output2() wont have to call skb_realloc_headroom() Reported-by: Brian Rak Tested-by: Brian Rak Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/macvtap.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index 919f4fccc322a9..4d050ee0f8723c 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -654,11 +654,14 @@ static void macvtap_skb_to_vnet_hdr(struct macvtap_queue *q, } /* else everything is zero */ } +/* Neighbour code has some assumptions on HH_DATA_MOD alignment */ +#define MACVTAP_RESERVE HH_DATA_OFF(ETH_HLEN) + /* Get packet from user space buffer */ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m, struct iov_iter *from, int noblock) { - int good_linear = SKB_MAX_HEAD(NET_IP_ALIGN); + int good_linear = SKB_MAX_HEAD(MACVTAP_RESERVE); struct sk_buff *skb; struct macvlan_dev *vlan; unsigned long total_len = iov_iter_count(from); @@ -722,7 +725,7 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m, linear = macvtap16_to_cpu(q, vnet_hdr.hdr_len); } - skb = macvtap_alloc_skb(&q->sk, NET_IP_ALIGN, copylen, + skb = macvtap_alloc_skb(&q->sk, MACVTAP_RESERVE, copylen, linear, noblock, &err); if (!skb) goto err; From 001f8cee4a23a1822bad0b3eae0d16ee3cb6834c Mon Sep 17 00:00:00 2001 From: Jaedon Shin Date: Sat, 28 Feb 2015 11:48:26 +0900 Subject: [PATCH 204/788] net: bcmgenet: fix throughtput regression [ Upstream commit 4092e6acf5cb16f56154e2dd22d647023dc3d646 ] This patch adds bcmgenet_tx_poll for the tx_rings. This can reduce the interrupt load and send xmit in network stack on time. This also separated for the completion of tx_ring16 from bcmgenet_poll. The bcmgenet_tx_reclaim of tx_ring[{0,1,2,3}] operative by an interrupt is to be not more than a certain number TxBDs. It is caused by too slowly reclaiming the transmitted skb. Therefore, performance degradation of xmit after 605ad7f ("tcp: refine TSO autosizing"). Signed-off-by: Jaedon Shin Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- .../net/ethernet/broadcom/genet/bcmgenet.c | 113 +++++++++++++----- .../net/ethernet/broadcom/genet/bcmgenet.h | 2 + 2 files changed, 88 insertions(+), 27 deletions(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index ff83c46bc38961..2874a004f815e9 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -971,13 +971,14 @@ static inline void bcmgenet_tx_ring_int_disable(struct bcmgenet_priv *priv, } /* Unlocked version of the reclaim routine */ -static void __bcmgenet_tx_reclaim(struct net_device *dev, - struct bcmgenet_tx_ring *ring) +static unsigned int __bcmgenet_tx_reclaim(struct net_device *dev, + struct bcmgenet_tx_ring *ring) { struct bcmgenet_priv *priv = netdev_priv(dev); int last_tx_cn, last_c_index, num_tx_bds; struct enet_cb *tx_cb_ptr; struct netdev_queue *txq; + unsigned int pkts_compl = 0; unsigned int bds_compl; unsigned int c_index; @@ -1005,6 +1006,7 @@ static void __bcmgenet_tx_reclaim(struct net_device *dev, tx_cb_ptr = ring->cbs + last_c_index; bds_compl = 0; if (tx_cb_ptr->skb) { + pkts_compl++; bds_compl = skb_shinfo(tx_cb_ptr->skb)->nr_frags + 1; dev->stats.tx_bytes += tx_cb_ptr->skb->len; dma_unmap_single(&dev->dev, @@ -1028,23 +1030,45 @@ static void __bcmgenet_tx_reclaim(struct net_device *dev, last_c_index &= (num_tx_bds - 1); } - if (ring->free_bds > (MAX_SKB_FRAGS + 1)) - ring->int_disable(priv, ring); - - if (netif_tx_queue_stopped(txq)) - netif_tx_wake_queue(txq); + if (ring->free_bds > (MAX_SKB_FRAGS + 1)) { + if (netif_tx_queue_stopped(txq)) + netif_tx_wake_queue(txq); + } ring->c_index = c_index; + + return pkts_compl; } -static void bcmgenet_tx_reclaim(struct net_device *dev, +static unsigned int bcmgenet_tx_reclaim(struct net_device *dev, struct bcmgenet_tx_ring *ring) { + unsigned int released; unsigned long flags; spin_lock_irqsave(&ring->lock, flags); - __bcmgenet_tx_reclaim(dev, ring); + released = __bcmgenet_tx_reclaim(dev, ring); spin_unlock_irqrestore(&ring->lock, flags); + + return released; +} + +static int bcmgenet_tx_poll(struct napi_struct *napi, int budget) +{ + struct bcmgenet_tx_ring *ring = + container_of(napi, struct bcmgenet_tx_ring, napi); + unsigned int work_done = 0; + + work_done = bcmgenet_tx_reclaim(ring->priv->dev, ring); + + if (work_done == 0) { + napi_complete(napi); + ring->int_enable(ring->priv, ring); + + return 0; + } + + return budget; } static void bcmgenet_tx_reclaim_all(struct net_device *dev) @@ -1302,10 +1326,8 @@ static netdev_tx_t bcmgenet_xmit(struct sk_buff *skb, struct net_device *dev) bcmgenet_tdma_ring_writel(priv, ring->index, ring->prod_index, TDMA_PROD_INDEX); - if (ring->free_bds <= (MAX_SKB_FRAGS + 1)) { + if (ring->free_bds <= (MAX_SKB_FRAGS + 1)) netif_tx_stop_queue(txq); - ring->int_enable(priv, ring); - } out: spin_unlock_irqrestore(&ring->lock, flags); @@ -1621,6 +1643,7 @@ static int init_umac(struct bcmgenet_priv *priv) struct device *kdev = &priv->pdev->dev; int ret; u32 reg, cpu_mask_clear; + int index; dev_dbg(&priv->pdev->dev, "bcmgenet: init_umac\n"); @@ -1647,7 +1670,7 @@ static int init_umac(struct bcmgenet_priv *priv) bcmgenet_intr_disable(priv); - cpu_mask_clear = UMAC_IRQ_RXDMA_BDONE; + cpu_mask_clear = UMAC_IRQ_RXDMA_BDONE | UMAC_IRQ_TXDMA_BDONE; dev_dbg(kdev, "%s:Enabling RXDMA_BDONE interrupt\n", __func__); @@ -1674,6 +1697,10 @@ static int init_umac(struct bcmgenet_priv *priv) bcmgenet_intrl2_0_writel(priv, cpu_mask_clear, INTRL2_CPU_MASK_CLEAR); + for (index = 0; index < priv->hw_params->tx_queues; index++) + bcmgenet_intrl2_1_writel(priv, (1 << index), + INTRL2_CPU_MASK_CLEAR); + /* Enable rx/tx engine.*/ dev_dbg(kdev, "done init umac\n"); @@ -1693,6 +1720,8 @@ static void bcmgenet_init_tx_ring(struct bcmgenet_priv *priv, unsigned int first_bd; spin_lock_init(&ring->lock); + ring->priv = priv; + netif_napi_add(priv->dev, &ring->napi, bcmgenet_tx_poll, 64); ring->index = index; if (index == DESC_INDEX) { ring->queue = 0; @@ -1738,6 +1767,17 @@ static void bcmgenet_init_tx_ring(struct bcmgenet_priv *priv, TDMA_WRITE_PTR); bcmgenet_tdma_ring_writel(priv, index, end_ptr * words_per_bd - 1, DMA_END_ADDR); + + napi_enable(&ring->napi); +} + +static void bcmgenet_fini_tx_ring(struct bcmgenet_priv *priv, + unsigned int index) +{ + struct bcmgenet_tx_ring *ring = &priv->tx_rings[index]; + + napi_disable(&ring->napi); + netif_napi_del(&ring->napi); } /* Initialize a RDMA ring */ @@ -1907,7 +1947,7 @@ static int bcmgenet_dma_teardown(struct bcmgenet_priv *priv) return ret; } -static void bcmgenet_fini_dma(struct bcmgenet_priv *priv) +static void __bcmgenet_fini_dma(struct bcmgenet_priv *priv) { int i; @@ -1926,6 +1966,18 @@ static void bcmgenet_fini_dma(struct bcmgenet_priv *priv) kfree(priv->tx_cbs); } +static void bcmgenet_fini_dma(struct bcmgenet_priv *priv) +{ + int i; + + bcmgenet_fini_tx_ring(priv, DESC_INDEX); + + for (i = 0; i < priv->hw_params->tx_queues; i++) + bcmgenet_fini_tx_ring(priv, i); + + __bcmgenet_fini_dma(priv); +} + /* init_edma: Initialize DMA control register */ static int bcmgenet_init_dma(struct bcmgenet_priv *priv) { @@ -1952,7 +2004,7 @@ static int bcmgenet_init_dma(struct bcmgenet_priv *priv) priv->tx_cbs = kcalloc(priv->num_tx_bds, sizeof(struct enet_cb), GFP_KERNEL); if (!priv->tx_cbs) { - bcmgenet_fini_dma(priv); + __bcmgenet_fini_dma(priv); return -ENOMEM; } @@ -1975,9 +2027,6 @@ static int bcmgenet_poll(struct napi_struct *napi, int budget) struct bcmgenet_priv, napi); unsigned int work_done; - /* tx reclaim */ - bcmgenet_tx_reclaim(priv->dev, &priv->tx_rings[DESC_INDEX]); - work_done = bcmgenet_desc_rx(priv, budget); /* Advancing our consumer index*/ @@ -2022,28 +2071,34 @@ static void bcmgenet_irq_task(struct work_struct *work) static irqreturn_t bcmgenet_isr1(int irq, void *dev_id) { struct bcmgenet_priv *priv = dev_id; + struct bcmgenet_tx_ring *ring; unsigned int index; /* Save irq status for bottom-half processing. */ priv->irq1_stat = bcmgenet_intrl2_1_readl(priv, INTRL2_CPU_STAT) & - ~priv->int1_mask; + ~bcmgenet_intrl2_1_readl(priv, INTRL2_CPU_MASK_STATUS); /* clear interrupts */ bcmgenet_intrl2_1_writel(priv, priv->irq1_stat, INTRL2_CPU_CLEAR); netif_dbg(priv, intr, priv->dev, "%s: IRQ=0x%x\n", __func__, priv->irq1_stat); + /* Check the MBDONE interrupts. * packet is done, reclaim descriptors */ - if (priv->irq1_stat & 0x0000ffff) { - index = 0; - for (index = 0; index < 16; index++) { - if (priv->irq1_stat & (1 << index)) - bcmgenet_tx_reclaim(priv->dev, - &priv->tx_rings[index]); + for (index = 0; index < priv->hw_params->tx_queues; index++) { + if (!(priv->irq1_stat & BIT(index))) + continue; + + ring = &priv->tx_rings[index]; + + if (likely(napi_schedule_prep(&ring->napi))) { + ring->int_disable(priv, ring); + __napi_schedule(&ring->napi); } } + return IRQ_HANDLED; } @@ -2075,8 +2130,12 @@ static irqreturn_t bcmgenet_isr0(int irq, void *dev_id) } if (priv->irq0_stat & (UMAC_IRQ_TXDMA_BDONE | UMAC_IRQ_TXDMA_PDONE)) { - /* Tx reclaim */ - bcmgenet_tx_reclaim(priv->dev, &priv->tx_rings[DESC_INDEX]); + struct bcmgenet_tx_ring *ring = &priv->tx_rings[DESC_INDEX]; + + if (likely(napi_schedule_prep(&ring->napi))) { + ring->int_disable(priv, ring); + __napi_schedule(&ring->napi); + } } if (priv->irq0_stat & (UMAC_IRQ_PHY_DET_R | UMAC_IRQ_PHY_DET_F | diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.h b/drivers/net/ethernet/broadcom/genet/bcmgenet.h index b36ddec0cc0a3c..0d370d168aee0e 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.h +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.h @@ -520,6 +520,7 @@ struct bcmgenet_hw_params { struct bcmgenet_tx_ring { spinlock_t lock; /* ring lock */ + struct napi_struct napi; /* NAPI per tx queue */ unsigned int index; /* ring index */ unsigned int queue; /* queue index */ struct enet_cb *cbs; /* tx ring buffer control block*/ @@ -534,6 +535,7 @@ struct bcmgenet_tx_ring { struct bcmgenet_tx_ring *); void (*int_disable)(struct bcmgenet_priv *priv, struct bcmgenet_tx_ring *); + struct bcmgenet_priv *priv; }; /* device context */ From 8e0f5ee1f98b0d703482d6a013f8026789e619cb Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Sat, 28 Feb 2015 18:09:16 -0800 Subject: [PATCH 205/788] net: bcmgenet: fix software maintained statistics [ Upstream commit f62ba9c14b85a682b64a4c421f91de0bd2aa8538 ] Commit 44c8bc3ce39f ("net: bcmgenet: log RX buffer allocation and RX/TX dma failures") added a few software maintained statistics using BCMGENET_STAT_MIB_RX and BCMGENET_STAT_MIB_TX. These statistics are read from the hardware MIB counters, such that bcmgenet_update_mib_counters() was trying to read from a non-existing MIB offset for these counters. Fix this by introducing a special type: BCMGENET_STAT_SOFT, similar to BCMGENET_STAT_NETDEV, such that bcmgenet_get_ethtool_stats will read from the software mib. Fixes: 44c8bc3ce39f ("net: bcmgenet: log RX buffer allocation and RX/TX dma failures") Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 2874a004f815e9..6befde61c20346 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -487,6 +487,7 @@ enum bcmgenet_stat_type { BCMGENET_STAT_MIB_TX, BCMGENET_STAT_RUNT, BCMGENET_STAT_MISC, + BCMGENET_STAT_SOFT, }; struct bcmgenet_stats { @@ -515,6 +516,7 @@ struct bcmgenet_stats { #define STAT_GENET_MIB_RX(str, m) STAT_GENET_MIB(str, m, BCMGENET_STAT_MIB_RX) #define STAT_GENET_MIB_TX(str, m) STAT_GENET_MIB(str, m, BCMGENET_STAT_MIB_TX) #define STAT_GENET_RUNT(str, m) STAT_GENET_MIB(str, m, BCMGENET_STAT_RUNT) +#define STAT_GENET_SOFT_MIB(str, m) STAT_GENET_MIB(str, m, BCMGENET_STAT_SOFT) #define STAT_GENET_MISC(str, m, offset) { \ .stat_string = str, \ @@ -614,9 +616,9 @@ static const struct bcmgenet_stats bcmgenet_gstrings_stats[] = { UMAC_RBUF_OVFL_CNT), STAT_GENET_MISC("rbuf_err_cnt", mib.rbuf_err_cnt, UMAC_RBUF_ERR_CNT), STAT_GENET_MISC("mdf_err_cnt", mib.mdf_err_cnt, UMAC_MDF_ERR_CNT), - STAT_GENET_MIB_RX("alloc_rx_buff_failed", mib.alloc_rx_buff_failed), - STAT_GENET_MIB_RX("rx_dma_failed", mib.rx_dma_failed), - STAT_GENET_MIB_TX("tx_dma_failed", mib.tx_dma_failed), + STAT_GENET_SOFT_MIB("alloc_rx_buff_failed", mib.alloc_rx_buff_failed), + STAT_GENET_SOFT_MIB("rx_dma_failed", mib.rx_dma_failed), + STAT_GENET_SOFT_MIB("tx_dma_failed", mib.tx_dma_failed), }; #define BCMGENET_STATS_LEN ARRAY_SIZE(bcmgenet_gstrings_stats) @@ -668,6 +670,7 @@ static void bcmgenet_update_mib_counters(struct bcmgenet_priv *priv) s = &bcmgenet_gstrings_stats[i]; switch (s->type) { case BCMGENET_STAT_NETDEV: + case BCMGENET_STAT_SOFT: continue; case BCMGENET_STAT_MIB_RX: case BCMGENET_STAT_MIB_TX: From d97191b06b7971ce1da4eb695896981dfdf1c8aa Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 27 Feb 2015 17:16:26 +0100 Subject: [PATCH 206/788] sh_eth: Fix lost MAC address on kexec [ Upstream commit a14c7d15ca91b444e77df08b916befdce77562ab ] Commit 740c7f31c094703c ("sh_eth: Ensure DMA engines are stopped before freeing buffers") added a call to sh_eth_reset() to the sh_eth_set_ringparam() and sh_eth_close() paths. However, setting the software reset bit(s) in the EDMR register resets the MAC Address Registers to zero. Hence after kexec, the new kernel doesn't detect a valid MAC address and assigns a random MAC address, breaking DHCP. Set the MAC address again after the reset in sh_eth_dev_exit() to fix this. Tested on r8a7740/armadillo (GETHER) and r8a7791/koelsch (FAST_RCAR). Fixes: 740c7f31c094703c ("sh_eth: Ensure DMA engines are stopped before freeing buffers") Signed-off-by: Geert Uytterhoeven Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/renesas/sh_eth.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 04283fe0e6a724..20f54d5f840946 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -1392,6 +1392,9 @@ static void sh_eth_dev_exit(struct net_device *ndev) msleep(2); /* max frame time at 10 Mbps < 1250 us */ sh_eth_get_stats(ndev); sh_eth_reset(ndev); + + /* Set MAC address again */ + update_mac_address(ndev); } /* free Tx skb function */ From 25ba5bb0d71737c85ba2bb3b63069ef751396814 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 27 Feb 2015 09:42:50 -0800 Subject: [PATCH 207/788] net: do not use rcu in rtnl_dump_ifinfo() [ Upstream commit cac5e65e8a7ea074f2626d2eaa53aa308452dec4 ] We did a failed attempt in the past to only use rcu in rtnl dump operations (commit e67f88dd12f6 "net: dont hold rtnl mutex during netlink dump callbacks") Now that dumps are holding RTNL anyway, there is no need to also use rcu locking, as it forbids any scheduling ability, like GFP_KERNEL allocations that controlling path should use instead of GFP_ATOMIC whenever possible. This should fix following splat Cong Wang reported : [ INFO: suspicious RCU usage. ] 3.19.0+ #805 Tainted: G W include/linux/rcupdate.h:538 Illegal context switch in RCU read-side critical section! other info that might help us debug this: rcu_scheduler_active = 1, debug_locks = 0 2 locks held by ip/771: #0: (rtnl_mutex){+.+.+.}, at: [] netlink_dump+0x21/0x26c #1: (rcu_read_lock){......}, at: [] rcu_read_lock+0x0/0x6e stack backtrace: CPU: 3 PID: 771 Comm: ip Tainted: G W 3.19.0+ #805 Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 0000000000000001 ffff8800d51e7718 ffffffff81a27457 0000000029e729e6 ffff8800d6108000 ffff8800d51e7748 ffffffff810b539b ffffffff820013dd 00000000000001c8 0000000000000000 ffff8800d7448088 ffff8800d51e7758 Call Trace: [] dump_stack+0x4c/0x65 [] lockdep_rcu_suspicious+0x107/0x110 [] rcu_preempt_sleep_check+0x45/0x47 [] ___might_sleep+0x1d/0x1cb [] __might_sleep+0x78/0x80 [] idr_alloc+0x45/0xd1 [] ? rcu_read_lock_held+0x3b/0x3d [] ? idr_for_each+0x53/0x101 [] alloc_netid+0x61/0x69 [] __peernet2id+0x79/0x8d [] peernet2id+0x13/0x1f [] rtnl_fill_ifinfo+0xa8d/0xc20 [] ? __lock_is_held+0x39/0x52 [] rtnl_dump_ifinfo+0x149/0x213 [] netlink_dump+0xef/0x26c [] netlink_recvmsg+0x17b/0x2c5 [] __sock_recvmsg+0x4e/0x59 [] sock_recvmsg+0x3f/0x51 [] ___sys_recvmsg+0xf6/0x1d9 [] ? handle_pte_fault+0x6e1/0xd3d [] ? native_sched_clock+0x35/0x37 [] ? sched_clock_local+0x12/0x72 [] ? sched_clock_cpu+0x9e/0xb7 [] ? rcu_read_lock_held+0x3b/0x3d [] ? __fcheck_files+0x4c/0x58 [] ? __fget_light+0x2d/0x52 [] __sys_recvmsg+0x42/0x60 [] SyS_recvmsg+0x12/0x1c Signed-off-by: Eric Dumazet Fixes: 0c7aecd4bde4b7302 ("netns: add rtnl cmd to add and get peer netns ids") Cc: Nicolas Dichtel Reported-by: Cong Wang Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/core/rtnetlink.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index d582e67a6ee01c..76ec6c52e3a3fd 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1274,7 +1274,6 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) s_h = cb->args[0]; s_idx = cb->args[1]; - rcu_read_lock(); cb->seq = net->dev_base_seq; /* A hack to preserve kernel<->userspace interface. @@ -1296,7 +1295,7 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { idx = 0; head = &net->dev_index_head[h]; - hlist_for_each_entry_rcu(dev, head, index_hlist) { + hlist_for_each_entry(dev, head, index_hlist) { if (idx < s_idx) goto cont; err = rtnl_fill_ifinfo(skb, dev, RTM_NEWLINK, @@ -1318,7 +1317,6 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) } } out: - rcu_read_unlock(); cb->args[1] = idx; cb->args[0] = h; From da122a6bc08e9d764e5c7750c9893211847a69d2 Mon Sep 17 00:00:00 2001 From: Ben Shelton Date: Mon, 16 Feb 2015 13:47:06 -0600 Subject: [PATCH 208/788] usb: plusb: Add support for National Instruments host-to-host cable [ Upstream commit 42c972a1f390e3bc51ca1e434b7e28764992067f ] The National Instruments USB Host-to-Host Cable is based on the Prolific PL-25A1 chipset. Add its VID/PID so the plusb driver will recognize it. Signed-off-by: Ben Shelton Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/usb/plusb.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/usb/plusb.c b/drivers/net/usb/plusb.c index 3d18bb0eee8528..1bfe0fcaccf5ba 100644 --- a/drivers/net/usb/plusb.c +++ b/drivers/net/usb/plusb.c @@ -134,6 +134,11 @@ static const struct usb_device_id products [] = { }, { USB_DEVICE(0x050d, 0x258a), /* Belkin F5U258/F5U279 (PL-25A1) */ .driver_info = (unsigned long) &prolific_info, +}, { + USB_DEVICE(0x3923, 0x7825), /* National Instruments USB + * Host-to-Host Cable + */ + .driver_info = (unsigned long) &prolific_info, }, { }, // END From 123303d42ce123e99354f5fd448edc157bbf04c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Kube=C4=8Dek?= Date: Mon, 2 Mar 2015 18:27:11 +0100 Subject: [PATCH 209/788] udp: only allow UFO for packets from SOCK_DGRAM sockets [ Upstream commit acf8dd0a9d0b9e4cdb597c2f74802f79c699e802 ] If an over-MTU UDP datagram is sent through a SOCK_RAW socket to a UFO-capable device, ip_ufo_append_data() sets skb->ip_summed to CHECKSUM_PARTIAL unconditionally as all GSO code assumes transport layer checksum is to be computed on segmentation. However, in this case, skb->csum_start and skb->csum_offset are never set as raw socket transmit path bypasses udp_send_skb() where they are usually set. As a result, driver may access invalid memory when trying to calculate the checksum and store the result (as observed in virtio_net driver). Moreover, the very idea of modifying the userspace provided UDP header is IMHO against raw socket semantics (I wasn't able to find a document clearly stating this or the opposite, though). And while allowing CHECKSUM_NONE in the UFO case would be more efficient, it would be a bit too intrusive change just to handle a corner case like this. Therefore disallowing UFO for packets from SOCK_DGRAM seems to be the best option. Signed-off-by: Michal Kubecek Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/ip_output.c | 3 ++- net/ipv6/ip6_output.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index c373c0708d9799..2e2f687ef8a2ac 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -890,7 +890,8 @@ static int __ip_append_data(struct sock *sk, cork->length += length; if (((length > mtu) || (skb && skb_is_gso(skb))) && (sk->sk_protocol == IPPROTO_UDP) && - (rt->dst.dev->features & NETIF_F_UFO) && !rt->dst.header_len) { + (rt->dst.dev->features & NETIF_F_UFO) && !rt->dst.header_len && + (sk->sk_type == SOCK_DGRAM)) { err = ip_ufo_append_data(sk, queue, getfrag, from, length, hh_len, fragheaderlen, transhdrlen, maxfraglen, flags); diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index d28f2a2efb32e4..3f5aa99590769c 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1283,7 +1283,8 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, if (((length > mtu) || (skb && skb_is_gso(skb))) && (sk->sk_protocol == IPPROTO_UDP) && - (rt->dst.dev->features & NETIF_F_UFO)) { + (rt->dst.dev->features & NETIF_F_UFO) && + (sk->sk_type == SOCK_DGRAM)) { err = ip6_ufo_append_data(sk, getfrag, from, length, hh_len, fragheaderlen, transhdrlen, mtu, flags, rt); From 2391f6b4220bd41b40be920c0f9ed0dc22f914b8 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Tue, 3 Mar 2015 23:16:16 +0900 Subject: [PATCH 210/788] net: ping: Return EAFNOSUPPORT when appropriate. [ Upstream commit 9145736d4862145684009d6a72a6e61324a9439e ] 1. For an IPv4 ping socket, ping_check_bind_addr does not check the family of the socket address that's passed in. Instead, make it behave like inet_bind, which enforces either that the address family is AF_INET, or that the family is AF_UNSPEC and the address is 0.0.0.0. 2. For an IPv6 ping socket, ping_check_bind_addr returns EINVAL if the socket family is not AF_INET6. Return EAFNOSUPPORT instead, for consistency with inet6_bind. 3. Make ping_v4_sendmsg and ping_v6_sendmsg return EAFNOSUPPORT instead of EINVAL if an incorrect socket address structure is passed in. 4. Make IPv6 ping sockets be IPv6-only. The code does not support IPv4, and it cannot easily be made to support IPv4 because the protocol numbers for ICMP and ICMPv6 are different. This makes connect(::ffff:192.0.2.1) fail with EAFNOSUPPORT instead of making the socket unusable. Among other things, this fixes an oops that can be triggered by: int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); struct sockaddr_in6 sin6 = { .sin6_family = AF_INET6, .sin6_addr = in6addr_any, }; bind(s, (struct sockaddr *) &sin6, sizeof(sin6)); Change-Id: If06ca86d9f1e4593c0d6df174caca3487c57a241 Signed-off-by: Lorenzo Colitti Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/ping.c | 12 ++++++++++-- net/ipv6/ping.c | 5 +++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 2a3720fb5a5ff5..0ae28f517a9b53 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -259,6 +259,9 @@ int ping_init_sock(struct sock *sk) kgid_t low, high; int ret = 0; + if (sk->sk_family == AF_INET6) + sk->sk_ipv6only = 1; + inet_get_ping_group_range_net(net, &low, &high); if (gid_lte(low, group) && gid_lte(group, high)) return 0; @@ -305,6 +308,11 @@ static int ping_check_bind_addr(struct sock *sk, struct inet_sock *isk, if (addr_len < sizeof(*addr)) return -EINVAL; + if (addr->sin_family != AF_INET && + !(addr->sin_family == AF_UNSPEC && + addr->sin_addr.s_addr == htonl(INADDR_ANY))) + return -EAFNOSUPPORT; + pr_debug("ping_check_bind_addr(sk=%p,addr=%pI4,port=%d)\n", sk, &addr->sin_addr.s_addr, ntohs(addr->sin_port)); @@ -330,7 +338,7 @@ static int ping_check_bind_addr(struct sock *sk, struct inet_sock *isk, return -EINVAL; if (addr->sin6_family != AF_INET6) - return -EINVAL; + return -EAFNOSUPPORT; pr_debug("ping_check_bind_addr(sk=%p,addr=%pI6c,port=%d)\n", sk, addr->sin6_addr.s6_addr, ntohs(addr->sin6_port)); @@ -716,7 +724,7 @@ static int ping_v4_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m if (msg->msg_namelen < sizeof(*usin)) return -EINVAL; if (usin->sin_family != AF_INET) - return -EINVAL; + return -EAFNOSUPPORT; daddr = usin->sin_addr.s_addr; /* no remote port */ } else { diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c index 2d3148378a1f6a..fe7e3e40349972 100644 --- a/net/ipv6/ping.c +++ b/net/ipv6/ping.c @@ -102,9 +102,10 @@ int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, if (msg->msg_name) { DECLARE_SOCKADDR(struct sockaddr_in6 *, u, msg->msg_name); - if (msg->msg_namelen < sizeof(struct sockaddr_in6) || - u->sin6_family != AF_INET6) { + if (msg->msg_namelen < sizeof(*u)) return -EINVAL; + if (u->sin6_family != AF_INET6) { + return -EAFNOSUPPORT; } if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != u->sin6_scope_id) { From dde0b1d5470c7aa302cb82a9565ea626f90d3f28 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 4 Mar 2015 08:36:31 +0100 Subject: [PATCH 211/788] team: don't traverse port list using rcu in team_set_mac_address [ Upstream commit 9215f437b85da339a7dfe3db6e288637406f88b2 ] Currently the list is traversed using rcu variant. That is not correct since dev_set_mac_address can be called which eventually calls rtmsg_ifinfo_build_skb and there, skb allocation can sleep. So fix this by remove the rcu usage here. Fixes: 3d249d4ca7 "net: introduce ethernet teaming device" Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/team/team.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 221ba86323d15b..2c087efed4737c 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -1736,11 +1736,11 @@ static int team_set_mac_address(struct net_device *dev, void *p) if (dev->type == ARPHRD_ETHER && !is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); - rcu_read_lock(); - list_for_each_entry_rcu(port, &team->port_list, list) + mutex_lock(&team->lock); + list_for_each_entry(port, &team->port_list, list) if (team->ops.port_change_dev_addr) team->ops.port_change_dev_addr(team, port); - rcu_read_unlock(); + mutex_unlock(&team->lock); return 0; } From 75809873eb40f3297dca579b043b26bce0666a14 Mon Sep 17 00:00:00 2001 From: Naoya Horiguchi Date: Wed, 11 Feb 2015 15:25:25 -0800 Subject: [PATCH 212/788] mm/hugetlb: fix getting refcount 0 page in hugetlb_fault() commit 0f792cf949a0be506c2aa8bfac0605746b146dda upstream. When running the test which causes the race as shown in the previous patch, we can hit the BUG "get_page() on refcount 0 page" in hugetlb_fault(). This race happens when pte turns into migration entry just after the first check of is_hugetlb_entry_migration() in hugetlb_fault() passed with false. To fix this, we need to check pte_present() again after huge_ptep_get(). This patch also reorders taking ptl and doing pte_page(), because pte_page() should be done in ptl. Due to this reordering, we need use trylock_page() in page != pagecache_page case to respect locking order. Fixes: 66aebce747ea ("hugetlb: fix race condition in hugetlb_fault()") Signed-off-by: Naoya Horiguchi Cc: Hugh Dickins Cc: James Hogan Cc: David Rientjes Cc: Mel Gorman Cc: Johannes Weiner Cc: Michal Hocko Cc: Rik van Riel Cc: Andrea Arcangeli Cc: Luiz Capitulino Cc: Nishanth Aravamudan Cc: Lee Schermerhorn Cc: Steve Capper Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/hugetlb.c | 52 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index c49586f407584e..61f807733c80d9 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -3134,6 +3134,7 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma, struct page *pagecache_page = NULL; struct hstate *h = hstate_vma(vma); struct address_space *mapping; + int need_wait_lock = 0; address &= huge_page_mask(h); @@ -3171,6 +3172,16 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma, ret = 0; + /* + * entry could be a migration/hwpoison entry at this point, so this + * check prevents the kernel from going below assuming that we have + * a active hugepage in pagecache. This goto expects the 2nd page fault, + * and is_hugetlb_entry_(migration|hwpoisoned) check will properly + * handle it. + */ + if (!pte_present(entry)) + goto out_mutex; + /* * If we are going to COW the mapping later, we examine the pending * reservations for this page now. This will ensure that any @@ -3190,30 +3201,31 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma, vma, address); } + ptl = huge_pte_lock(h, mm, ptep); + + /* Check for a racing update before calling hugetlb_cow */ + if (unlikely(!pte_same(entry, huge_ptep_get(ptep)))) + goto out_ptl; + /* * hugetlb_cow() requires page locks of pte_page(entry) and * pagecache_page, so here we need take the former one * when page != pagecache_page or !pagecache_page. - * Note that locking order is always pagecache_page -> page, - * so no worry about deadlock. */ page = pte_page(entry); - get_page(page); if (page != pagecache_page) - lock_page(page); - - ptl = huge_pte_lockptr(h, mm, ptep); - spin_lock(ptl); - /* Check for a racing update before calling hugetlb_cow */ - if (unlikely(!pte_same(entry, huge_ptep_get(ptep)))) - goto out_ptl; + if (!trylock_page(page)) { + need_wait_lock = 1; + goto out_ptl; + } + get_page(page); if (flags & FAULT_FLAG_WRITE) { if (!huge_pte_write(entry)) { ret = hugetlb_cow(mm, vma, address, ptep, entry, pagecache_page, ptl); - goto out_ptl; + goto out_put_page; } entry = huge_pte_mkdirty(entry); } @@ -3221,7 +3233,10 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma, if (huge_ptep_set_access_flags(vma, address, ptep, entry, flags & FAULT_FLAG_WRITE)) update_mmu_cache(vma, address, ptep); - +out_put_page: + if (page != pagecache_page) + unlock_page(page); + put_page(page); out_ptl: spin_unlock(ptl); @@ -3229,12 +3244,17 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma, unlock_page(pagecache_page); put_page(pagecache_page); } - if (page != pagecache_page) - unlock_page(page); - put_page(page); - out_mutex: mutex_unlock(&htlb_fault_mutex_table[hash]); + /* + * Generally it's safe to hold refcount during waiting page lock. But + * here we just wait to defer the next page fault to avoid busy loop and + * the page is not used after unlocked before returning from the current + * page fault. So we are safe from accessing freed page, even if we wait + * here without taking refcount. + */ + if (need_wait_lock) + wait_on_page_locked(page); return ret; } From 2c90c58c731c1877391e13da28a33d5dc7f813b1 Mon Sep 17 00:00:00 2001 From: Naoya Horiguchi Date: Wed, 11 Feb 2015 15:25:28 -0800 Subject: [PATCH 213/788] mm/hugetlb: add migration/hwpoisoned entry check in hugetlb_change_protection commit a8bda28d87c38c6aa93de28ba5d30cc18e865a11 upstream. There is a race condition between hugepage migration and change_protection(), where hugetlb_change_protection() doesn't care about migration entries and wrongly overwrites them. That causes unexpected results like kernel crash. HWPoison entries also can cause the same problem. This patch adds is_hugetlb_entry_(migration|hwpoisoned) check in this function to do proper actions. Fixes: 290408d4a2 ("hugetlb: hugepage migration core") Signed-off-by: Naoya Horiguchi Cc: Hugh Dickins Cc: James Hogan Cc: David Rientjes Cc: Mel Gorman Cc: Johannes Weiner Cc: Michal Hocko Cc: Rik van Riel Cc: Andrea Arcangeli Cc: Luiz Capitulino Cc: Nishanth Aravamudan Cc: Lee Schermerhorn Cc: Steve Capper Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/hugetlb.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 61f807733c80d9..7c873208728847 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -3384,7 +3384,26 @@ unsigned long hugetlb_change_protection(struct vm_area_struct *vma, spin_unlock(ptl); continue; } - if (!huge_pte_none(huge_ptep_get(ptep))) { + pte = huge_ptep_get(ptep); + if (unlikely(is_hugetlb_entry_hwpoisoned(pte))) { + spin_unlock(ptl); + continue; + } + if (unlikely(is_hugetlb_entry_migration(pte))) { + swp_entry_t entry = pte_to_swp_entry(pte); + + if (is_write_migration_entry(entry)) { + pte_t newpte; + + make_migration_entry_read(&entry); + newpte = swp_entry_to_pte(entry); + set_huge_pte_at(mm, address, ptep, newpte); + pages++; + } + spin_unlock(ptl); + continue; + } + if (!huge_pte_none(pte)) { pte = huge_ptep_get_and_clear(mm, address, ptep); pte = pte_mkhuge(huge_pte_modify(pte, newprot)); pte = arch_make_huge_pte(pte, vma, NULL, 0); From 3ffc797a2722ae09fdc2167b316c695f0b903e91 Mon Sep 17 00:00:00 2001 From: Naoya Horiguchi Date: Wed, 11 Feb 2015 15:25:32 -0800 Subject: [PATCH 214/788] mm/hugetlb: add migration entry check in __unmap_hugepage_range commit 9fbc1f635fd0bd28cb32550211bf095753ac637a upstream. If __unmap_hugepage_range() tries to unmap the address range over which hugepage migration is on the way, we get the wrong page because pte_page() doesn't work for migration entries. This patch simply clears the pte for migration entries as we do for hwpoison entries. Fixes: 290408d4a2 ("hugetlb: hugepage migration core") Signed-off-by: Naoya Horiguchi Cc: Hugh Dickins Cc: James Hogan Cc: David Rientjes Cc: Mel Gorman Cc: Johannes Weiner Cc: Michal Hocko Cc: Rik van Riel Cc: Andrea Arcangeli Cc: Luiz Capitulino Cc: Nishanth Aravamudan Cc: Lee Schermerhorn Cc: Steve Capper Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/hugetlb.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 7c873208728847..267e4197110079 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -2657,9 +2657,10 @@ void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma, goto unlock; /* - * HWPoisoned hugepage is already unmapped and dropped reference + * Migrating hugepage or HWPoisoned hugepage is already + * unmapped and its refcount is dropped, so just clear pte here. */ - if (unlikely(is_hugetlb_entry_hwpoisoned(pte))) { + if (unlikely(!pte_present(pte))) { huge_pte_clear(mm, address, ptep); goto unlock; } From 89316deb0dd1bbc8c4eff6b005ce66f30f378112 Mon Sep 17 00:00:00 2001 From: Andrey Ryabinin Date: Tue, 10 Feb 2015 14:11:33 -0800 Subject: [PATCH 215/788] mm, hugetlb: remove unnecessary lower bound on sysctl handlers"? commit 3cd7645de624939c38f5124b4ac15f8b35a1a8b7 upstream. Commit ed4d4902ebdd ("mm, hugetlb: remove hugetlb_zero and hugetlb_infinity") replaced 'unsigned long hugetlb_zero' with 'int zero' leading to out-of-bounds access in proc_doulongvec_minmax(). Use '.extra1 = NULL' instead of '.extra1 = &zero'. Passing NULL is equivalent to passing minimal value, which is 0 for unsigned types. Fixes: ed4d4902ebdd ("mm, hugetlb: remove hugetlb_zero and hugetlb_infinity") Signed-off-by: Andrey Ryabinin Reported-by: Dmitry Vyukov Suggested-by: Manfred Spraul Acked-by: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- kernel/sysctl.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 137c7f69b2642f..88ea2d6e003140 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1248,7 +1248,6 @@ static struct ctl_table vm_table[] = { .maxlen = sizeof(unsigned long), .mode = 0644, .proc_handler = hugetlb_sysctl_handler, - .extra1 = &zero, }, #ifdef CONFIG_NUMA { @@ -1257,7 +1256,6 @@ static struct ctl_table vm_table[] = { .maxlen = sizeof(unsigned long), .mode = 0644, .proc_handler = &hugetlb_mempolicy_sysctl_handler, - .extra1 = &zero, }, #endif { @@ -1280,7 +1278,6 @@ static struct ctl_table vm_table[] = { .maxlen = sizeof(unsigned long), .mode = 0644, .proc_handler = hugetlb_overcommit_handler, - .extra1 = &zero, }, #endif { From cdf476685b5c9dcd20f0305791bd60212cace25d Mon Sep 17 00:00:00 2001 From: Vlastimil Babka Date: Wed, 11 Feb 2015 15:28:15 -0800 Subject: [PATCH 216/788] mm: when stealing freepages, also take pages created by splitting buddy page commit 99592d598eca62bdbbf62b59941c189176dfc614 upstream. When studying page stealing, I noticed some weird looking decisions in try_to_steal_freepages(). The first I assume is a bug (Patch 1), the following two patches were driven by evaluation. Testing was done with stress-highalloc of mmtests, using the mm_page_alloc_extfrag tracepoint and postprocessing to get counts of how often page stealing occurs for individual migratetypes, and what migratetypes are used for fallbacks. Arguably, the worst case of page stealing is when UNMOVABLE allocation steals from MOVABLE pageblock. RECLAIMABLE allocation stealing from MOVABLE allocation is also not ideal, so the goal is to minimize these two cases. The evaluation of v2 wasn't always clear win and Joonsoo questioned the results. Here I used different baseline which includes RFC compaction improvements from [1]. I found that the compaction improvements reduce variability of stress-highalloc, so there's less noise in the data. First, let's look at stress-highalloc configured to do sync compaction, and how these patches reduce page stealing events during the test. First column is after fresh reboot, other two are reiterations of test without reboot. That was all accumulater over 5 re-iterations (so the benchmark was run 5x3 times with 5 fresh restarts). Baseline: 3.19-rc4 3.19-rc4 3.19-rc4 5-nothp-1 5-nothp-2 5-nothp-3 Page alloc extfrag event 10264225 8702233 10244125 Extfrag fragmenting 10263271 8701552 10243473 Extfrag fragmenting for unmovable 13595 17616 15960 Extfrag fragmenting unmovable placed with movable 7989 12193 8447 Extfrag fragmenting for reclaimable 658 1840 1817 Extfrag fragmenting reclaimable placed with movable 558 1677 1679 Extfrag fragmenting for movable 10249018 8682096 10225696 With Patch 1: 3.19-rc4 3.19-rc4 3.19-rc4 6-nothp-1 6-nothp-2 6-nothp-3 Page alloc extfrag event 11834954 9877523 9774860 Extfrag fragmenting 11833993 9876880 9774245 Extfrag fragmenting for unmovable 7342 16129 11712 Extfrag fragmenting unmovable placed with movable 4191 10547 6270 Extfrag fragmenting for reclaimable 373 1130 923 Extfrag fragmenting reclaimable placed with movable 302 906 738 Extfrag fragmenting for movable 11826278 9859621 9761610 With Patch 2: 3.19-rc4 3.19-rc4 3.19-rc4 7-nothp-1 7-nothp-2 7-nothp-3 Page alloc extfrag event 4725990 3668793 3807436 Extfrag fragmenting 4725104 3668252 3806898 Extfrag fragmenting for unmovable 6678 7974 7281 Extfrag fragmenting unmovable placed with movable 2051 3829 4017 Extfrag fragmenting for reclaimable 429 1208 1278 Extfrag fragmenting reclaimable placed with movable 369 976 1034 Extfrag fragmenting for movable 4717997 3659070 3798339 With Patch 3: 3.19-rc4 3.19-rc4 3.19-rc4 8-nothp-1 8-nothp-2 8-nothp-3 Page alloc extfrag event 5016183 4700142 3850633 Extfrag fragmenting 5015325 4699613 3850072 Extfrag fragmenting for unmovable 1312 3154 3088 Extfrag fragmenting unmovable placed with movable 1115 2777 2714 Extfrag fragmenting for reclaimable 437 1193 1097 Extfrag fragmenting reclaimable placed with movable 330 969 879 Extfrag fragmenting for movable 5013576 4695266 3845887 In v2 we've seen apparent regression with Patch 1 for unmovable events, this is now gone, suggesting it was indeed noise. Here, each patch improves the situation for unmovable events. Reclaimable is improved by patch 1 and then either the same modulo noise, or perhaps sligtly worse - a small price for unmovable improvements, IMHO. The number of movable allocations falling back to other migratetypes is most noisy, but it's reduced to half at Patch 2 nevertheless. These are least critical as compaction can move them around. If we look at success rates, the patches don't affect them, that didn't change. Baseline: 3.19-rc4 3.19-rc4 3.19-rc4 5-nothp-1 5-nothp-2 5-nothp-3 Success 1 Min 49.00 ( 0.00%) 42.00 ( 14.29%) 41.00 ( 16.33%) Success 1 Mean 51.00 ( 0.00%) 45.00 ( 11.76%) 42.60 ( 16.47%) Success 1 Max 55.00 ( 0.00%) 51.00 ( 7.27%) 46.00 ( 16.36%) Success 2 Min 53.00 ( 0.00%) 47.00 ( 11.32%) 44.00 ( 16.98%) Success 2 Mean 59.60 ( 0.00%) 50.80 ( 14.77%) 48.20 ( 19.13%) Success 2 Max 64.00 ( 0.00%) 56.00 ( 12.50%) 52.00 ( 18.75%) Success 3 Min 84.00 ( 0.00%) 82.00 ( 2.38%) 78.00 ( 7.14%) Success 3 Mean 85.60 ( 0.00%) 82.80 ( 3.27%) 79.40 ( 7.24%) Success 3 Max 86.00 ( 0.00%) 83.00 ( 3.49%) 80.00 ( 6.98%) Patch 1: 3.19-rc4 3.19-rc4 3.19-rc4 6-nothp-1 6-nothp-2 6-nothp-3 Success 1 Min 49.00 ( 0.00%) 44.00 ( 10.20%) 44.00 ( 10.20%) Success 1 Mean 51.80 ( 0.00%) 46.00 ( 11.20%) 45.80 ( 11.58%) Success 1 Max 54.00 ( 0.00%) 49.00 ( 9.26%) 49.00 ( 9.26%) Success 2 Min 58.00 ( 0.00%) 49.00 ( 15.52%) 48.00 ( 17.24%) Success 2 Mean 60.40 ( 0.00%) 51.80 ( 14.24%) 50.80 ( 15.89%) Success 2 Max 63.00 ( 0.00%) 54.00 ( 14.29%) 55.00 ( 12.70%) Success 3 Min 84.00 ( 0.00%) 81.00 ( 3.57%) 79.00 ( 5.95%) Success 3 Mean 85.00 ( 0.00%) 81.60 ( 4.00%) 79.80 ( 6.12%) Success 3 Max 86.00 ( 0.00%) 82.00 ( 4.65%) 82.00 ( 4.65%) Patch 2: 3.19-rc4 3.19-rc4 3.19-rc4 7-nothp-1 7-nothp-2 7-nothp-3 Success 1 Min 50.00 ( 0.00%) 44.00 ( 12.00%) 39.00 ( 22.00%) Success 1 Mean 52.80 ( 0.00%) 45.60 ( 13.64%) 42.40 ( 19.70%) Success 1 Max 55.00 ( 0.00%) 46.00 ( 16.36%) 47.00 ( 14.55%) Success 2 Min 52.00 ( 0.00%) 48.00 ( 7.69%) 45.00 ( 13.46%) Success 2 Mean 53.40 ( 0.00%) 49.80 ( 6.74%) 48.80 ( 8.61%) Success 2 Max 57.00 ( 0.00%) 52.00 ( 8.77%) 52.00 ( 8.77%) Success 3 Min 84.00 ( 0.00%) 81.00 ( 3.57%) 79.00 ( 5.95%) Success 3 Mean 85.00 ( 0.00%) 82.40 ( 3.06%) 79.60 ( 6.35%) Success 3 Max 86.00 ( 0.00%) 83.00 ( 3.49%) 80.00 ( 6.98%) Patch 3: 3.19-rc4 3.19-rc4 3.19-rc4 8-nothp-1 8-nothp-2 8-nothp-3 Success 1 Min 46.00 ( 0.00%) 44.00 ( 4.35%) 42.00 ( 8.70%) Success 1 Mean 50.20 ( 0.00%) 45.60 ( 9.16%) 44.00 ( 12.35%) Success 1 Max 52.00 ( 0.00%) 47.00 ( 9.62%) 47.00 ( 9.62%) Success 2 Min 53.00 ( 0.00%) 49.00 ( 7.55%) 48.00 ( 9.43%) Success 2 Mean 55.80 ( 0.00%) 50.60 ( 9.32%) 49.00 ( 12.19%) Success 2 Max 59.00 ( 0.00%) 52.00 ( 11.86%) 51.00 ( 13.56%) Success 3 Min 84.00 ( 0.00%) 80.00 ( 4.76%) 79.00 ( 5.95%) Success 3 Mean 85.40 ( 0.00%) 81.60 ( 4.45%) 80.40 ( 5.85%) Success 3 Max 87.00 ( 0.00%) 83.00 ( 4.60%) 82.00 ( 5.75%) While there's no improvement here, I consider reduced fragmentation events to be worth on its own. Patch 2 also seems to reduce scanning for free pages, and migrations in compaction, suggesting it has somewhat less work to do: Patch 1: Compaction stalls 4153 3959 3978 Compaction success 1523 1441 1446 Compaction failures 2630 2517 2531 Page migrate success 4600827 4943120 5104348 Page migrate failure 19763 16656 17806 Compaction pages isolated 9597640 10305617 10653541 Compaction migrate scanned 77828948 86533283 87137064 Compaction free scanned 517758295 521312840 521462251 Compaction cost 5503 5932 6110 Patch 2: Compaction stalls 3800 3450 3518 Compaction success 1421 1316 1317 Compaction failures 2379 2134 2201 Page migrate success 4160421 4502708 4752148 Page migrate failure 19705 14340 14911 Compaction pages isolated 8731983 9382374 9910043 Compaction migrate scanned 98362797 96349194 98609686 Compaction free scanned 496512560 469502017 480442545 Compaction cost 5173 5526 5811 As with v2, /proc/pagetypeinfo appears unaffected with respect to numbers of unmovable and reclaimable pageblocks. Configuring the benchmark to allocate like THP page fault (i.e. no sync compaction) gives much noisier results for iterations 2 and 3 after reboot. This is not so surprising given how [1] offers lower improvements in this scenario due to less restarts after deferred compaction which would change compaction pivot. Baseline: 3.19-rc4 3.19-rc4 3.19-rc4 5-thp-1 5-thp-2 5-thp-3 Page alloc extfrag event 8148965 6227815 6646741 Extfrag fragmenting 8147872 6227130 6646117 Extfrag fragmenting for unmovable 10324 12942 15975 Extfrag fragmenting unmovable placed with movable 5972 8495 10907 Extfrag fragmenting for reclaimable 601 1707 2210 Extfrag fragmenting reclaimable placed with movable 520 1570 2000 Extfrag fragmenting for movable 8136947 6212481 6627932 Patch 1: 3.19-rc4 3.19-rc4 3.19-rc4 6-thp-1 6-thp-2 6-thp-3 Page alloc extfrag event 8345457 7574471 7020419 Extfrag fragmenting 8343546 7573777 7019718 Extfrag fragmenting for unmovable 10256 18535 30716 Extfrag fragmenting unmovable placed with movable 6893 11726 22181 Extfrag fragmenting for reclaimable 465 1208 1023 Extfrag fragmenting reclaimable placed with movable 353 996 843 Extfrag fragmenting for movable 8332825 7554034 6987979 Patch 2: 3.19-rc4 3.19-rc4 3.19-rc4 7-thp-1 7-thp-2 7-thp-3 Page alloc extfrag event 3512847 3020756 2891625 Extfrag fragmenting 3511940 3020185 2891059 Extfrag fragmenting for unmovable 9017 6892 6191 Extfrag fragmenting unmovable placed with movable 1524 3053 2435 Extfrag fragmenting for reclaimable 445 1081 1160 Extfrag fragmenting reclaimable placed with movable 375 918 986 Extfrag fragmenting for movable 3502478 3012212 2883708 Patch 3: 3.19-rc4 3.19-rc4 3.19-rc4 8-thp-1 8-thp-2 8-thp-3 Page alloc extfrag event 3181699 3082881 2674164 Extfrag fragmenting 3180812 3082303 2673611 Extfrag fragmenting for unmovable 1201 4031 4040 Extfrag fragmenting unmovable placed with movable 974 3611 3645 Extfrag fragmenting for reclaimable 478 1165 1294 Extfrag fragmenting reclaimable placed with movable 387 985 1030 Extfrag fragmenting for movable 3179133 3077107 2668277 The improvements for first iteration are clear, the rest is much noisier and can appear like regression for Patch 1. Anyway, patch 2 rectifies it. Allocation success rates are again unaffected so there's no point in making this e-mail any longer. [1] http://marc.info/?l=linux-mm&m=142166196321125&w=2 This patch (of 3): When __rmqueue_fallback() is called to allocate a page of order X, it will find a page of order Y >= X of a fallback migratetype, which is different from the desired migratetype. With the help of try_to_steal_freepages(), it may change the migratetype (to the desired one) also of: 1) all currently free pages in the pageblock containing the fallback page 2) the fallback pageblock itself 3) buddy pages created by splitting the fallback page (when Y > X) These decisions take the order Y into account, as well as the desired migratetype, with the goal of preventing multiple fallback allocations that could e.g. distribute UNMOVABLE allocations among multiple pageblocks. Originally, decision for 1) has implied the decision for 3). Commit 47118af076f6 ("mm: mmzone: MIGRATE_CMA migration type added") changed that (probably unintentionally) so that the buddy pages in case 3) are always changed to the desired migratetype, except for CMA pageblocks. Commit fef903efcf0c ("mm/page_allo.c: restructure free-page stealing code and fix a bug") did some refactoring and added a comment that the case of 3) is intended. Commit 0cbef29a7821 ("mm: __rmqueue_fallback() should respect pageblock type") removed the comment and tried to restore the original behavior where 1) implies 3), but due to the previous refactoring, the result is instead that only 2) implies 3) - and the conditions for 2) are less frequently met than conditions for 1). This may increase fragmentation in situations where the code decides to steal all free pages from the pageblock (case 1)), but then gives back the buddy pages produced by splitting. This patch restores the original intended logic where 1) implies 3). During testing with stress-highalloc from mmtests, this has shown to decrease the number of events where UNMOVABLE and RECLAIMABLE allocations steal from MOVABLE pageblocks, which can lead to permanent fragmentation. In some cases it has increased the number of events when MOVABLE allocations steal from UNMOVABLE or RECLAIMABLE pageblocks, but these are fixable by sync compaction and thus less harmful. Note that evaluation has shown that the behavior introduced by 47118af076f6 for buddy pages in case 3) is actually even better than the original logic, so the following patch will introduce it properly once again. For stable backports of this patch it makes thus sense to only fix versions containing 0cbef29a7821. [iamjoonsoo.kim@lge.com: tracepoint fix] Signed-off-by: Vlastimil Babka Acked-by: Mel Gorman Cc: Zhang Yanfei Acked-by: Minchan Kim Cc: David Rientjes Cc: Rik van Riel Cc: "Aneesh Kumar K.V" Cc: "Kirill A. Shutemov" Cc: Johannes Weiner Cc: Joonsoo Kim Cc: Michal Hocko Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- include/trace/events/kmem.h | 7 ++++--- mm/page_alloc.c | 12 +++++------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/include/trace/events/kmem.h b/include/trace/events/kmem.h index aece1346ceb741..4ad10baecd4dcd 100644 --- a/include/trace/events/kmem.h +++ b/include/trace/events/kmem.h @@ -268,11 +268,11 @@ TRACE_EVENT(mm_page_alloc_extfrag, TP_PROTO(struct page *page, int alloc_order, int fallback_order, - int alloc_migratetype, int fallback_migratetype, int new_migratetype), + int alloc_migratetype, int fallback_migratetype), TP_ARGS(page, alloc_order, fallback_order, - alloc_migratetype, fallback_migratetype, new_migratetype), + alloc_migratetype, fallback_migratetype), TP_STRUCT__entry( __field( struct page *, page ) @@ -289,7 +289,8 @@ TRACE_EVENT(mm_page_alloc_extfrag, __entry->fallback_order = fallback_order; __entry->alloc_migratetype = alloc_migratetype; __entry->fallback_migratetype = fallback_migratetype; - __entry->change_ownership = (new_migratetype == alloc_migratetype); + __entry->change_ownership = (alloc_migratetype == + get_pageblock_migratetype(page)); ), TP_printk("page=%p pfn=%lu alloc_order=%d fallback_order=%d pageblock_order=%d alloc_migratetype=%d fallback_migratetype=%d fragmenting=%d change_ownership=%d", diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 8e20f9c2fa5ab7..f363511ecf48f2 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -1138,8 +1138,8 @@ static void change_pageblock_range(struct page *pageblock_page, * nor move CMA pages to different free lists. We don't want unmovable pages * to be allocated from MIGRATE_CMA areas. * - * Returns the new migratetype of the pageblock (or the same old migratetype - * if it was unchanged). + * Returns the allocation migratetype if free pages were stolen, or the + * fallback migratetype if it was decided not to steal. */ static int try_to_steal_freepages(struct zone *zone, struct page *page, int start_type, int fallback_type) @@ -1170,12 +1170,10 @@ static int try_to_steal_freepages(struct zone *zone, struct page *page, /* Claim the whole block if over half of it is free */ if (pages >= (1 << (pageblock_order-1)) || - page_group_by_mobility_disabled) { - + page_group_by_mobility_disabled) set_pageblock_migratetype(page, start_type); - return start_type; - } + return start_type; } return fallback_type; @@ -1227,7 +1225,7 @@ __rmqueue_fallback(struct zone *zone, unsigned int order, int start_migratetype) set_freepage_migratetype(page, new_type); trace_mm_page_alloc_extfrag(page, order, current_order, - start_migratetype, migratetype, new_type); + start_migratetype, migratetype); return page; } From 00f4f16b8e60acbba7de443a12ba3aa6c0de6d14 Mon Sep 17 00:00:00 2001 From: Roman Gushchin Date: Wed, 11 Feb 2015 15:28:39 -0800 Subject: [PATCH 217/788] mm/mmap.c: fix arithmetic overflow in __vm_enough_memory() commit 5703b087dc8eaf47bfb399d6cf512d471beff405 upstream. I noticed, that "allowed" can easily overflow by falling below 0, because (total_vm / 32) can be larger than "allowed". The problem occurs in OVERCOMMIT_NONE mode. In this case, a huge allocation can success and overcommit the system (despite OVERCOMMIT_NONE mode). All subsequent allocations will fall (system-wide), so system become unusable. The problem was masked out by commit c9b1d0981fcc ("mm: limit growth of 3% hardcoded other user reserve"), but it's easy to reproduce it on older kernels: 1) set overcommit_memory sysctl to 2 2) mmap() large file multiple times (with VM_SHARED flag) 3) try to malloc() large amount of memory It also can be reproduced on newer kernels, but miss-configured sysctl_user_reserve_kbytes is required. Fix this issue by switching to signed arithmetic here. [akpm@linux-foundation.org: use min_t] Signed-off-by: Roman Gushchin Cc: Andrew Shewmaker Cc: Rik van Riel Cc: Konstantin Khlebnikov Reviewed-by: Michal Hocko Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/mmap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mm/mmap.c b/mm/mmap.c index 7f684d5a808738..e5cc3ca1d869f3 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -152,7 +152,7 @@ EXPORT_SYMBOL_GPL(vm_memory_committed); */ int __vm_enough_memory(struct mm_struct *mm, long pages, int cap_sys_admin) { - unsigned long free, allowed, reserve; + long free, allowed, reserve; VM_WARN_ONCE(percpu_counter_read(&vm_committed_as) < -(s64)vm_committed_as_batch * num_online_cpus(), @@ -220,7 +220,7 @@ int __vm_enough_memory(struct mm_struct *mm, long pages, int cap_sys_admin) */ if (mm) { reserve = sysctl_user_reserve_kbytes >> (PAGE_SHIFT - 10); - allowed -= min(mm->total_vm / 32, reserve); + allowed -= min_t(long, mm->total_vm / 32, reserve); } if (percpu_counter_read_positive(&vm_committed_as) < allowed) From 225c2a356300344510af5ee6c20a88eb84cc8ff2 Mon Sep 17 00:00:00 2001 From: Roman Gushchin Date: Wed, 11 Feb 2015 15:28:42 -0800 Subject: [PATCH 218/788] mm/nommu.c: fix arithmetic overflow in __vm_enough_memory() commit 8138a67a5557ffea3a21dfd6f037842d4e748513 upstream. I noticed that "allowed" can easily overflow by falling below 0, because (total_vm / 32) can be larger than "allowed". The problem occurs in OVERCOMMIT_NONE mode. In this case, a huge allocation can success and overcommit the system (despite OVERCOMMIT_NONE mode). All subsequent allocations will fall (system-wide), so system become unusable. The problem was masked out by commit c9b1d0981fcc ("mm: limit growth of 3% hardcoded other user reserve"), but it's easy to reproduce it on older kernels: 1) set overcommit_memory sysctl to 2 2) mmap() large file multiple times (with VM_SHARED flag) 3) try to malloc() large amount of memory It also can be reproduced on newer kernels, but miss-configured sysctl_user_reserve_kbytes is required. Fix this issue by switching to signed arithmetic here. Signed-off-by: Roman Gushchin Cc: Andrew Shewmaker Cc: Rik van Riel Cc: Konstantin Khlebnikov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/nommu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mm/nommu.c b/mm/nommu.c index 28bd8c4dff6feb..9986f3bc99cb46 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -1895,7 +1895,7 @@ EXPORT_SYMBOL(unmap_mapping_range); */ int __vm_enough_memory(struct mm_struct *mm, long pages, int cap_sys_admin) { - unsigned long free, allowed, reserve; + long free, allowed, reserve; vm_acct_memory(pages); @@ -1959,7 +1959,7 @@ int __vm_enough_memory(struct mm_struct *mm, long pages, int cap_sys_admin) */ if (mm) { reserve = sysctl_user_reserve_kbytes >> (PAGE_SHIFT - 10); - allowed -= min(mm->total_vm / 32, reserve); + allowed -= min_t(long, mm->total_vm / 32, reserve); } if (percpu_counter_read_positive(&vm_committed_as) < allowed) From cf4a79699bbeb8d677aa5f3a145a066ead7b43c4 Mon Sep 17 00:00:00 2001 From: Joonsoo Kim Date: Thu, 12 Feb 2015 14:59:50 -0800 Subject: [PATCH 219/788] mm/compaction: fix wrong order check in compact_finished() commit 372549c2a3778fd3df445819811c944ad54609ca upstream. What we want to check here is whether there is highorder freepage in buddy list of other migratetype in order to steal it without fragmentation. But, current code just checks cc->order which means allocation request order. So, this is wrong. Without this fix, non-movable synchronous compaction below pageblock order would not stopped until compaction is complete, because migratetype of most pageblocks are movable and high order freepage made by compaction is usually on movable type buddy list. There is some report related to this bug. See below link. http://www.spinics.net/lists/linux-mm/msg81666.html Although the issued system still has load spike comes from compaction, this makes that system completely stable and responsive according to his report. stress-highalloc test in mmtests with non movable order 7 allocation doesn't show any notable difference in allocation success rate, but, it shows more compaction success rate. Compaction success rate (Compaction success * 100 / Compaction stalls, %) 18.47 : 28.94 Fixes: 1fb3f8ca0e92 ("mm: compaction: capture a suitable high-order page immediately when it is made available") Signed-off-by: Joonsoo Kim Acked-by: Vlastimil Babka Reviewed-by: Zhang Yanfei Cc: Mel Gorman Cc: David Rientjes Cc: Rik van Riel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/compaction.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/compaction.c b/mm/compaction.c index 546e571e9d60d1..d3efa975c817b4 100644 --- a/mm/compaction.c +++ b/mm/compaction.c @@ -1088,7 +1088,7 @@ static int compact_finished(struct zone *zone, struct compact_control *cc, return COMPACT_PARTIAL; /* Job done if allocation would set block type */ - if (cc->order >= pageblock_order && area->nr_free) + if (order >= pageblock_order && area->nr_free) return COMPACT_PARTIAL; } From 6f5468a717c8f18e721dc9bc7f8d7ce41f22eb1a Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Thu, 12 Feb 2015 15:00:19 -0800 Subject: [PATCH 220/788] mm/memory.c: actually remap enough memory commit 9cb12d7b4ccaa976f97ce0c5fd0f1b6a83bc2a75 upstream. For whatever reason, generic_access_phys() only remaps one page, but actually allows to access arbitrary size. It's quite easy to trigger large reads, like printing out large structure with gdb, which leads to a crash. Fix it by remapping correct size. Fixes: 28b2ee20c7cb ("access_process_vm device memory infrastructure") Signed-off-by: Grazvydas Ignotas Cc: Rik van Riel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/memory.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/memory.c b/mm/memory.c index 2c3536cc6c6327..6aa7822bb64d29 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -3561,7 +3561,7 @@ int generic_access_phys(struct vm_area_struct *vma, unsigned long addr, if (follow_phys(vma, addr, write, &prot, &phys_addr)) return -EINVAL; - maddr = ioremap_prot(phys_addr, PAGE_SIZE, prot); + maddr = ioremap_prot(phys_addr, PAGE_ALIGN(len + offset), prot); if (write) memcpy_toio(maddr + offset, buf, len); else From 1bab6ee0b41e4fa4550693da82742aa50a94290c Mon Sep 17 00:00:00 2001 From: Naoya Horiguchi Date: Thu, 12 Feb 2015 15:00:25 -0800 Subject: [PATCH 221/788] mm: hwpoison: drop lru_add_drain_all() in __soft_offline_page() commit 9ab3b598d2dfbdb0153ffa7e4b1456bbff59a25d upstream. A race condition starts to be visible in recent mmotm, where a PG_hwpoison flag is set on a migration source page *before* it's back in buddy page poo= l. This is problematic because no page flag is supposed to be set when freeing (see __free_one_page().) So the user-visible effect of this race is that it could trigger the BUG_ON() when soft-offlining is called. The root cause is that we call lru_add_drain_all() to make sure that the page is in buddy, but that doesn't work because this function just schedule= s a work item and doesn't wait its completion. drain_all_pages() does drainin= g directly, so simply dropping lru_add_drain_all() solves this problem. Fixes: f15bdfa802bf ("mm/memory-failure.c: fix memory leak in successful soft offlining") Signed-off-by: Naoya Horiguchi Cc: Andi Kleen Cc: Tony Luck Cc: Chen Gong Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/memory-failure.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/mm/memory-failure.c b/mm/memory-failure.c index feb803bf344364..20c29ddff17b2b 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -1653,8 +1653,6 @@ static int __soft_offline_page(struct page *page, int flags) * source page should be freed back to buddy before * setting PG_hwpoison. */ - if (!is_free_buddy_page(page)) - lru_add_drain_all(); if (!is_free_buddy_page(page)) drain_all_pages(page_zone(page)); SetPageHWPoison(page); From 2cd12f3d5799add8af547cec707404c9ad053c76 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Thu, 12 Feb 2015 15:00:28 -0800 Subject: [PATCH 222/788] mm: fix negative nr_isolated counts commit ff59909a077b3c51c168cb658601c6b63136a347 upstream. The vmstat interfaces are good at hiding negative counts (at least when CONFIG_SMP); but if you peer behind the curtain, you find that nr_isolated_anon and nr_isolated_file soon go negative, and grow ever more negative: so they can absorb larger and larger numbers of isolated pages, yet still appear to be zero. I'm happy to avoid a congestion_wait() when too_many_isolated() myself; but I guess it's there for a good reason, in which case we ought to get too_many_isolated() working again. The imbalance comes from isolate_migratepages()'s ISOLATE_ABORT case: putback_movable_pages() decrements the NR_ISOLATED counts, but we forgot to call acct_isolated() to increment them. It is possible that the bug whcih this patch fixes could cause OOM kills when the system still has a lot of reclaimable page cache. Fixes: edc2ca612496 ("mm, compaction: move pageblock checks up from isolate_migratepages_range()") Signed-off-by: Hugh Dickins Acked-by: Vlastimil Babka Acked-by: Joonsoo Kim Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/compaction.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mm/compaction.c b/mm/compaction.c index d3efa975c817b4..91357ff6d3a847 100644 --- a/mm/compaction.c +++ b/mm/compaction.c @@ -1015,8 +1015,10 @@ static isolate_migrate_t isolate_migratepages(struct zone *zone, low_pfn = isolate_migratepages_block(cc, low_pfn, end_pfn, isolate_mode); - if (!low_pfn || cc->contended) + if (!low_pfn || cc->contended) { + acct_isolated(zone, cc); return ISOLATE_ABORT; + } /* * Either we isolated something and proceed with migration. Or From 51571a010cdd78d90ab74eed34303cabe2d97539 Mon Sep 17 00:00:00 2001 From: Joonsoo Kim Date: Fri, 27 Feb 2015 15:51:43 -0800 Subject: [PATCH 223/788] mm/nommu: fix memory leak commit da616534ed7f6e8ffaab699258b55c8d78d0b4ea upstream. Maxime reported the following memory leak regression due to commit dbc8358c7237 ("mm/nommu: use alloc_pages_exact() rather than its own implementation"). On v3.19, I am facing a memory leak. Each time I run a command one page is lost. Here an example with busybox's free command: / # free total used free shared buffers cached Mem: 7928 1972 5956 0 0 492 -/+ buffers/cache: 1480 6448 / # free total used free shared buffers cached Mem: 7928 1976 5952 0 0 492 -/+ buffers/cache: 1484 6444 / # free total used free shared buffers cached Mem: 7928 1980 5948 0 0 492 -/+ buffers/cache: 1488 6440 / # free total used free shared buffers cached Mem: 7928 1984 5944 0 0 492 -/+ buffers/cache: 1492 6436 / # free total used free shared buffers cached Mem: 7928 1988 5940 0 0 492 -/+ buffers/cache: 1496 6432 At some point, the system fails to sastisfy 256KB allocations: free: page allocation failure: order:6, mode:0xd0 CPU: 0 PID: 67 Comm: free Not tainted 3.19.0-05389-gacf2cf1-dirty #64 Hardware name: STM32 (Device Tree Support) show_stack+0xb/0xc warn_alloc_failed+0x97/0xbc __alloc_pages_nodemask+0x295/0x35c __get_free_pages+0xb/0x24 alloc_pages_exact+0x19/0x24 do_mmap_pgoff+0x423/0x658 vm_mmap_pgoff+0x3f/0x4e load_flat_file+0x20d/0x4f8 load_flat_binary+0x3f/0x26c search_binary_handler+0x51/0xe4 do_execveat_common+0x271/0x35c do_execve+0x19/0x1c ret_fast_syscall+0x1/0x4a Mem-info: Normal per-cpu: CPU 0: hi: 0, btch: 1 usd: 0 active_anon:0 inactive_anon:0 isolated_anon:0 active_file:0 inactive_file:0 isolated_file:0 unevictable:123 dirty:0 writeback:0 unstable:0 free:1515 slab_reclaimable:17 slab_unreclaimable:139 mapped:0 shmem:0 pagetables:0 bounce:0 free_cma:0 Normal free:6060kB min:352kB low:440kB high:528kB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:492kB isolated(anon):0ks lowmem_reserve[]: 0 0 Normal: 23*4kB (U) 22*8kB (U) 24*16kB (U) 23*32kB (U) 23*64kB (U) 23*128kB (U) 1*256kB (U) 0*512kB 0*1024kB 0*2048kB 0*4096kB = 6060kB 123 total pagecache pages 2048 pages of RAM 1538 free pages 66 reserved pages 109 slab pages -46 pages shared 0 pages swap cached nommu: Allocation of length 221184 from process 67 (free) failed Normal per-cpu: CPU 0: hi: 0, btch: 1 usd: 0 active_anon:0 inactive_anon:0 isolated_anon:0 active_file:0 inactive_file:0 isolated_file:0 unevictable:123 dirty:0 writeback:0 unstable:0 free:1515 slab_reclaimable:17 slab_unreclaimable:139 mapped:0 shmem:0 pagetables:0 bounce:0 free_cma:0 Normal free:6060kB min:352kB low:440kB high:528kB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:492kB isolated(anon):0ks lowmem_reserve[]: 0 0 Normal: 23*4kB (U) 22*8kB (U) 24*16kB (U) 23*32kB (U) 23*64kB (U) 23*128kB (U) 1*256kB (U) 0*512kB 0*1024kB 0*2048kB 0*4096kB = 6060kB 123 total pagecache pages Unable to allocate RAM for process text/data, errno 12 SEGV This problem happens because we allocate ordered page through __get_free_pages() in do_mmap_private() in some cases and we try to free individual pages rather than ordered page in free_page_series(). In this case, freeing pages whose refcount is not 0 won't be freed to the page allocator so memory leak happens. To fix the problem, this patch changes __get_free_pages() to alloc_pages_exact() since alloc_pages_exact() returns physically-contiguous pages but each pages are refcounted. Fixes: dbc8358c7237 ("mm/nommu: use alloc_pages_exact() rather than its own implementation"). Reported-by: Maxime Coquelin Tested-by: Maxime Coquelin Signed-off-by: Joonsoo Kim Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/nommu.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mm/nommu.c b/mm/nommu.c index 9986f3bc99cb46..ae5baae8e212ba 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -1189,11 +1189,9 @@ static int do_mmap_private(struct vm_area_struct *vma, if (sysctl_nr_trim_pages && total - point >= sysctl_nr_trim_pages) { total = point; kdebug("try to alloc exact %lu pages", total); - base = alloc_pages_exact(len, GFP_KERNEL); - } else { - base = (void *)__get_free_pages(GFP_KERNEL, order); } + base = alloc_pages_exact(total << PAGE_SHIFT, GFP_KERNEL); if (!base) goto enomem; From 6c749954dcd0b852f5d7a127eff72716cd7f9ca6 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Fri, 27 Feb 2015 15:52:09 -0800 Subject: [PATCH 224/788] mm: page_alloc: revert inadvertent !__GFP_FS retry behavior change commit cc87317726f851531ae8422e0c2d3d6e2d7b1955 upstream. Historically, !__GFP_FS allocations were not allowed to invoke the OOM killer once reclaim had failed, but nevertheless kept looping in the allocator. Commit 9879de7373fc ("mm: page_alloc: embed OOM killing naturally into allocation slowpath"), which should have been a simple cleanup patch, accidentally changed the behavior to aborting the allocation at that point. This creates problems with filesystem callers (?) that currently rely on the allocator waiting for other tasks to intervene. Revert the behavior as it shouldn't have been changed as part of a cleanup patch. Fixes: 9879de7373fc ("mm: page_alloc: embed OOM killing naturally into allocation slowpath") Signed-off-by: Johannes Weiner Acked-by: Michal Hocko Reported-by: Tetsuo Handa Cc: Theodore Ts'o Cc: Dave Chinner Acked-by: David Rientjes Cc: Oleg Nesterov Cc: Mel Gorman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/page_alloc.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index f363511ecf48f2..8bbef06de720b0 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2380,8 +2380,15 @@ __alloc_pages_may_oom(gfp_t gfp_mask, unsigned int order, if (high_zoneidx < ZONE_NORMAL) goto out; /* The OOM killer does not compensate for light reclaim */ - if (!(gfp_mask & __GFP_FS)) + if (!(gfp_mask & __GFP_FS)) { + /* + * XXX: Page reclaim didn't yield anything, + * and the OOM killer can't be invoked, but + * keep looping as per should_alloc_retry(). + */ + *did_some_progress = 1; goto out; + } /* * GFP_THISNODE contains __GFP_NORETRY and we never hit this. * Sanity check for bare calls of __GFP_THISNODE, not real OOM. From 65fee0e0a451415eca251c1b19e63cc62b054c45 Mon Sep 17 00:00:00 2001 From: David Ung Date: Tue, 20 Jan 2015 18:37:35 -0800 Subject: [PATCH 225/788] drm/tegra: Use correct relocation target offsets commit 31f40f86526b71009973854c1dfe799ee70f7588 upstream. When copying a relocation from userspace, copy the correct target offset. Signed-off-by: David Ung Fixes: 961e3beae3b2 ("drm/tegra: Make job submission 64-bit safe") [treding@nvidia.com: provide a better commit message] Signed-off-by: Thierry Reding Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/tegra/drm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index d4f827593dfa20..d8c9fb713ebb3b 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -190,7 +190,7 @@ static int host1x_reloc_copy_from_user(struct host1x_reloc *dest, if (err < 0) return err; - err = get_user(dest->target.offset, &src->cmdbuf.offset); + err = get_user(dest->target.offset, &src->target.offset); if (err < 0) return err; From 002ce3eac5a3ac7bb053a42adabc6cffdf6cb891 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Wed, 4 Feb 2015 10:19:51 +0900 Subject: [PATCH 226/788] drm/radeon: Don't try to enable write-combining without PAT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit a53fa43873b88bad15a2eb1f01dc5efa689625ce upstream. Doing so can cause things to become slow. Print a warning at compile time and an informative message at runtime in that case. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=88758 Reviewed-by: Christian König Signed-off-by: Michel Dänzer Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/radeon_object.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c index 86fc56434b2875..040d2847f8e844 100644 --- a/drivers/gpu/drm/radeon/radeon_object.c +++ b/drivers/gpu/drm/radeon/radeon_object.c @@ -238,6 +238,18 @@ int radeon_bo_create(struct radeon_device *rdev, * See https://bugs.freedesktop.org/show_bug.cgi?id=84627 */ bo->flags &= ~RADEON_GEM_GTT_WC; +#elif defined(CONFIG_X86) && !defined(CONFIG_X86_PAT) + /* Don't try to enable write-combining when it can't work, or things + * may be slow + * See https://bugs.freedesktop.org/show_bug.cgi?id=88758 + */ + +#warning Please enable CONFIG_MTRR and CONFIG_X86_PAT for better performance \ + thanks to write-combining + + DRM_INFO_ONCE("Please enable CONFIG_MTRR and CONFIG_X86_PAT for " + "better performance thanks to write-combining\n"); + bo->flags &= ~RADEON_GEM_GTT_WC; #endif radeon_ttm_placement_from_domain(bo, domain); From cec4e689a8daba945252db35b4debc683664e972 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 6 Feb 2015 12:53:27 -0500 Subject: [PATCH 227/788] drm/radeon: only enable kv/kb dpm interrupts once v3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 410af8d7285a0b96314845c75c39fd612b755688 upstream. Enable at init and disable on fini. Workaround for hardware problems. v2 (chk): extend commit message v3: add new function Signed-off-by: Alex Deucher Signed-off-by: Christian König (v2) Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/cik.c | 21 --------------------- drivers/gpu/drm/radeon/kv_dpm.c | 17 +++++++++++++++-- 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 64fdae558d36e9..13cf65e081fc9a 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -7334,7 +7334,6 @@ int cik_irq_set(struct radeon_device *rdev) u32 hpd1, hpd2, hpd3, hpd4, hpd5, hpd6; u32 grbm_int_cntl = 0; u32 dma_cntl, dma_cntl1; - u32 thermal_int; if (!rdev->irq.installed) { WARN(1, "Can't enable IRQ/MSI because no handler is installed\n"); @@ -7364,13 +7363,6 @@ int cik_irq_set(struct radeon_device *rdev) cp_m1p0 = RREG32(CP_ME1_PIPE0_INT_CNTL) & ~TIME_STAMP_INT_ENABLE; - if (rdev->flags & RADEON_IS_IGP) - thermal_int = RREG32_SMC(CG_THERMAL_INT_CTRL) & - ~(THERM_INTH_MASK | THERM_INTL_MASK); - else - thermal_int = RREG32_SMC(CG_THERMAL_INT) & - ~(THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW); - /* enable CP interrupts on all rings */ if (atomic_read(&rdev->irq.ring_int[RADEON_RING_TYPE_GFX_INDEX])) { DRM_DEBUG("cik_irq_set: sw int gfx\n"); @@ -7474,14 +7466,6 @@ int cik_irq_set(struct radeon_device *rdev) hpd6 |= DC_HPDx_INT_EN; } - if (rdev->irq.dpm_thermal) { - DRM_DEBUG("dpm thermal\n"); - if (rdev->flags & RADEON_IS_IGP) - thermal_int |= THERM_INTH_MASK | THERM_INTL_MASK; - else - thermal_int |= THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW; - } - WREG32(CP_INT_CNTL_RING0, cp_int_cntl); WREG32(SDMA0_CNTL + SDMA0_REGISTER_OFFSET, dma_cntl); @@ -7528,11 +7512,6 @@ int cik_irq_set(struct radeon_device *rdev) WREG32(DC_HPD5_INT_CONTROL, hpd5); WREG32(DC_HPD6_INT_CONTROL, hpd6); - if (rdev->flags & RADEON_IS_IGP) - WREG32_SMC(CG_THERMAL_INT_CTRL, thermal_int); - else - WREG32_SMC(CG_THERMAL_INT, thermal_int); - return 0; } diff --git a/drivers/gpu/drm/radeon/kv_dpm.c b/drivers/gpu/drm/radeon/kv_dpm.c index e3e9c10cfba974..85a109e1e56b7d 100644 --- a/drivers/gpu/drm/radeon/kv_dpm.c +++ b/drivers/gpu/drm/radeon/kv_dpm.c @@ -1169,6 +1169,19 @@ void kv_dpm_enable_bapm(struct radeon_device *rdev, bool enable) } } +static void kv_enable_thermal_int(struct radeon_device *rdev, bool enable) +{ + u32 thermal_int; + + thermal_int = RREG32_SMC(CG_THERMAL_INT_CTRL); + if (enable) + thermal_int |= THERM_INTH_MASK | THERM_INTL_MASK; + else + thermal_int &= ~(THERM_INTH_MASK | THERM_INTL_MASK); + WREG32_SMC(CG_THERMAL_INT_CTRL, thermal_int); + +} + int kv_dpm_enable(struct radeon_device *rdev) { struct kv_power_info *pi = kv_get_pi(rdev); @@ -1280,8 +1293,7 @@ int kv_dpm_late_enable(struct radeon_device *rdev) DRM_ERROR("kv_set_thermal_temperature_range failed\n"); return ret; } - rdev->irq.dpm_thermal = true; - radeon_irq_set(rdev); + kv_enable_thermal_int(rdev, true); } /* powerdown unused blocks for now */ @@ -1312,6 +1324,7 @@ void kv_dpm_disable(struct radeon_device *rdev) kv_stop_dpm(rdev); kv_enable_ulv(rdev, false); kv_reset_am(rdev); + kv_enable_thermal_int(rdev, false); kv_update_current_ps(rdev, rdev->pm.dpm.boot_ps); } From 52c35ffe609a225d465bcc49570817e7e443a7fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Tue, 10 Feb 2015 14:26:39 +0100 Subject: [PATCH 228/788] drm/radeon: workaround for CP HW bug on CIK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit a9c73a0e022c33954835e66fec3cd744af90ec98 upstream. Emit the EOP twice to avoid cache flushing problems. Signed-off-by: Christian König Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/cik.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 13cf65e081fc9a..de9a56205f0a1e 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -3904,7 +3904,21 @@ void cik_fence_gfx_ring_emit(struct radeon_device *rdev, struct radeon_ring *ring = &rdev->ring[fence->ring]; u64 addr = rdev->fence_drv[fence->ring].gpu_addr; - /* EVENT_WRITE_EOP - flush caches, send int */ + /* Workaround for cache flush problems. First send a dummy EOP + * event down the pipe with seq one below. + */ + radeon_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE_EOP, 4)); + radeon_ring_write(ring, (EOP_TCL1_ACTION_EN | + EOP_TC_ACTION_EN | + EVENT_TYPE(CACHE_FLUSH_AND_INV_TS_EVENT) | + EVENT_INDEX(5))); + radeon_ring_write(ring, addr & 0xfffffffc); + radeon_ring_write(ring, (upper_32_bits(addr) & 0xffff) | + DATA_SEL(1) | INT_SEL(0)); + radeon_ring_write(ring, fence->seq - 1); + radeon_ring_write(ring, 0); + + /* Then send the real EOP event down the pipe. */ radeon_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE_EOP, 4)); radeon_ring_write(ring, (EOP_TCL1_ACTION_EN | EOP_TC_ACTION_EN | From 3aeb57ffdb5325636869d102f70ef6096b9d49d9 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 11 Feb 2015 18:34:36 -0500 Subject: [PATCH 229/788] drm/radeon/dp: Set EDP_CONFIGURATION_SET for bridge chips if necessary commit 66c2b84ba6256bc5399eed45582af9ebb3ba2c15 upstream. Don't restrict it to just eDP panels. Some LVDS bridge chips require this. Fixes blank panels on resume on certain laptops. Noticed by mrnuke on IRC. bug: https://bugs.freedesktop.org/show_bug.cgi?id=42960 Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/atombios_dp.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index db42a670f9957c..5bf825dfaa098e 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -623,10 +623,8 @@ static int radeon_dp_link_train_init(struct radeon_dp_link_train_info *dp_info) drm_dp_dpcd_writeb(dp_info->aux, DP_DOWNSPREAD_CTRL, 0); - if ((dp_info->connector->connector_type == DRM_MODE_CONNECTOR_eDP) && - (dig->panel_mode == DP_PANEL_MODE_INTERNAL_DP2_MODE)) { + if (dig->panel_mode == DP_PANEL_MODE_INTERNAL_DP2_MODE) drm_dp_dpcd_writeb(dp_info->aux, DP_EDP_CONFIGURATION_SET, 1); - } /* set the lane count on the sink */ tmp = dp_info->dp_lane_count; From 12bc2f3df1d5c9445202a1eb9119460ca6928042 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 12 Feb 2015 00:40:58 -0500 Subject: [PATCH 230/788] drm/radeon: fix voltage setup on hawaii commit 09b6e85fc868568e1b2820235a2a851aecbccfcc upstream. Missing parameter when fetching the real voltage values from atom. Fixes problems with dynamic clocking on certain boards. bug: https://bugs.freedesktop.org/show_bug.cgi?id=87457 Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/radeon_atombios.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index dbc94f3002972d..fc1b3f34cf1827 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -3289,6 +3289,7 @@ int radeon_atom_get_voltage_evv(struct radeon_device *rdev, args.in.ucVoltageType = VOLTAGE_TYPE_VDDC; args.in.ucVoltageMode = ATOM_GET_VOLTAGE_EVV_VOLTAGE; + args.in.usVoltageLevel = cpu_to_le16(virtual_voltage_id); args.in.ulSCLKFreq = cpu_to_le32(rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries[entry_id].clk); From b16a8497a0342524a343d07a234508cc12b68443 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 22 Jan 2015 13:42:00 +0000 Subject: [PATCH 231/788] drm/i915: Insert a command barrier on BLT/BSD cache flushes commit f0a1fb10e5f79f5aaf8d7e94b9fa6bf2fa9aeebf upstream. This looked like an odd regression from commit ec5cc0f9b019af95e4571a9fa162d94294c8d90b Author: Chris Wilson Date: Thu Jun 12 10:28:55 2014 +0100 drm/i915: Restrict GPU boost to the RCS engine but in reality it undercovered a much older coherency bug. The issue that boosting the GPU frequency on the BCS ring was masking was that we could wake the CPU up after completion of a BCS batch and inspect memory prior to the write cache being fully evicted. In order to serialise the breadcrumb interrupt (and so ensure that the CPU's view of memory is coherent) we need to perform a post-sync operation in the MI_FLUSH_DW. v2: Fix all the MI_FLUSH_DW (bsd plus the duplication in execlists). Also fix the invalidate_domains mask in gen8_emit_flush() for ring != VCS. Testcase: gpuX-rcs-gpu-read-after-write Signed-off-by: Chris Wilson Acked-by: Daniel Vetter Signed-off-by: Jani Nikula Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/intel_lrc.c | 20 +++++++++++--------- drivers/gpu/drm/i915/intel_ringbuffer.c | 23 +++++++++++++++++++---- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index e588376227eaf6..5ebe805bb6273c 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -1237,15 +1237,17 @@ static int gen8_emit_flush(struct intel_ringbuffer *ringbuf, cmd = MI_FLUSH_DW + 1; - if (ring == &dev_priv->ring[VCS]) { - if (invalidate_domains & I915_GEM_GPU_DOMAINS) - cmd |= MI_INVALIDATE_TLB | MI_INVALIDATE_BSD | - MI_FLUSH_DW_STORE_INDEX | - MI_FLUSH_DW_OP_STOREDW; - } else { - if (invalidate_domains & I915_GEM_DOMAIN_RENDER) - cmd |= MI_INVALIDATE_TLB | MI_FLUSH_DW_STORE_INDEX | - MI_FLUSH_DW_OP_STOREDW; + /* We always require a command barrier so that subsequent + * commands, such as breadcrumb interrupts, are strictly ordered + * wrt the contents of the write cache being flushed to memory + * (and thus being coherent from the CPU). + */ + cmd |= MI_FLUSH_DW_STORE_INDEX | MI_FLUSH_DW_OP_STOREDW; + + if (invalidate_domains & I915_GEM_GPU_DOMAINS) { + cmd |= MI_INVALIDATE_TLB; + if (ring == &dev_priv->ring[VCS]) + cmd |= MI_INVALIDATE_BSD; } intel_logical_ring_emit(ringbuf, cmd); diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index c7bc93d28d84ec..a335b842b78bd6 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -2178,6 +2178,14 @@ static int gen6_bsd_ring_flush(struct intel_engine_cs *ring, cmd = MI_FLUSH_DW; if (INTEL_INFO(ring->dev)->gen >= 8) cmd += 1; + + /* We always require a command barrier so that subsequent + * commands, such as breadcrumb interrupts, are strictly ordered + * wrt the contents of the write cache being flushed to memory + * (and thus being coherent from the CPU). + */ + cmd |= MI_FLUSH_DW_STORE_INDEX | MI_FLUSH_DW_OP_STOREDW; + /* * Bspec vol 1c.5 - video engine command streamer: * "If ENABLED, all TLBs will be invalidated once the flush @@ -2185,8 +2193,8 @@ static int gen6_bsd_ring_flush(struct intel_engine_cs *ring, * Post-Sync Operation field is a value of 1h or 3h." */ if (invalidate & I915_GEM_GPU_DOMAINS) - cmd |= MI_INVALIDATE_TLB | MI_INVALIDATE_BSD | - MI_FLUSH_DW_STORE_INDEX | MI_FLUSH_DW_OP_STOREDW; + cmd |= MI_INVALIDATE_TLB | MI_INVALIDATE_BSD; + intel_ring_emit(ring, cmd); intel_ring_emit(ring, I915_GEM_HWS_SCRATCH_ADDR | MI_FLUSH_DW_USE_GTT); if (INTEL_INFO(ring->dev)->gen >= 8) { @@ -2282,6 +2290,14 @@ static int gen6_ring_flush(struct intel_engine_cs *ring, cmd = MI_FLUSH_DW; if (INTEL_INFO(ring->dev)->gen >= 8) cmd += 1; + + /* We always require a command barrier so that subsequent + * commands, such as breadcrumb interrupts, are strictly ordered + * wrt the contents of the write cache being flushed to memory + * (and thus being coherent from the CPU). + */ + cmd |= MI_FLUSH_DW_STORE_INDEX | MI_FLUSH_DW_OP_STOREDW; + /* * Bspec vol 1c.3 - blitter engine command streamer: * "If ENABLED, all TLBs will be invalidated once the flush @@ -2289,8 +2305,7 @@ static int gen6_ring_flush(struct intel_engine_cs *ring, * Post-Sync Operation field is a value of 1h or 3h." */ if (invalidate & I915_GEM_DOMAIN_RENDER) - cmd |= MI_INVALIDATE_TLB | MI_FLUSH_DW_STORE_INDEX | - MI_FLUSH_DW_OP_STOREDW; + cmd |= MI_INVALIDATE_TLB; intel_ring_emit(ring, cmd); intel_ring_emit(ring, I915_GEM_HWS_SCRATCH_ADDR | MI_FLUSH_DW_USE_GTT); if (INTEL_INFO(ring->dev)->gen >= 8) { From cae716d5ffa2e20546b9a974bf525270e9a66f84 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 24 Nov 2014 16:54:11 +0100 Subject: [PATCH 232/788] drm/i915: Drop vblank wait from intel_dp_link_down commit 0ca09685546fed5fc8f0535204f0626f352140f4 upstream. Nothing in Bspec seems to indicate that we actually needs this, and it looks like can't work since by this point the pipe is off and so vblanks won't really happen any more. Note that Bspec mentions that it takes a vblank for this bit to change, but _only_ when enabling. Dropping this code quenches an annoying backtrace introduced by the more anal checking since commit 51e31d49c89055299e34b8f44d13f70e19aaaad1 Author: Daniel Vetter Date: Mon Sep 15 12:36:02 2014 +0200 drm/i915: Use generic vblank wait Note: This fixes the fallout from the above commit, but does not address the shortcomings of the IBX transcoder select workaround implementation discussed during review [1]. [1] http://mid.gmane.org/87y4o7usxf.fsf@intel.com Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=86095 Signed-off-by: Daniel Vetter Reviewed-by: Paulo Zanoni Signed-off-by: Jani Nikula Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/intel_dp.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 5cecc20efa71df..9f6c8971855c4c 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -3515,8 +3515,6 @@ intel_dp_link_down(struct intel_dp *intel_dp) enum port port = intel_dig_port->port; struct drm_device *dev = intel_dig_port->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_crtc *intel_crtc = - to_intel_crtc(intel_dig_port->base.base.crtc); uint32_t DP = intel_dp->DP; if (WARN_ON(HAS_DDI(dev))) @@ -3541,8 +3539,6 @@ intel_dp_link_down(struct intel_dp *intel_dp) if (HAS_PCH_IBX(dev) && I915_READ(intel_dp->output_reg) & DP_PIPEB_SELECT) { - struct drm_crtc *crtc = intel_dig_port->base.base.crtc; - /* Hardware workaround: leaving our transcoder select * set to transcoder B while it's off will prevent the * corresponding HDMI output on transcoder A. @@ -3553,18 +3549,7 @@ intel_dp_link_down(struct intel_dp *intel_dp) */ DP &= ~DP_PIPEB_SELECT; I915_WRITE(intel_dp->output_reg, DP); - - /* Changes to enable or select take place the vblank - * after being written. - */ - if (WARN_ON(crtc == NULL)) { - /* We should never try to disable a port without a crtc - * attached. For paranoia keep the code around for a - * bit. */ - POSTING_READ(intel_dp->output_reg); - msleep(50); - } else - intel_wait_for_vblank(dev, intel_crtc->pipe); + POSTING_READ(intel_dp->output_reg); } DP &= ~DP_AUDIO_OUTPUT_ENABLE; From 885aa661b25a0ffc983aa27654829696660a616b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Winiarski?= Date: Tue, 3 Feb 2015 15:48:17 +0100 Subject: [PATCH 233/788] drm/i915: Prevent use-after-free in invalidate_range_start callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 460822b0b1a77db859b0320469799fa4dbe4d367 upstream. It's possible for invalidate_range_start mmu notifier callback to race against userptr object release. If the gem object was released prior to obtaining the spinlock in invalidate_range_start we're hitting null pointer dereference. Testcase: igt/gem_userptr_blits/stress-mm-invalidate-close Testcase: igt/gem_userptr_blits/stress-mm-invalidate-close-overlap Cc: Chris Wilson Signed-off-by: Michał Winiarski Reviewed-by: Chris Wilson [Jani: added code comment suggested by Chris] Signed-off-by: Jani Nikula Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/i915_gem_userptr.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_userptr.c b/drivers/gpu/drm/i915/i915_gem_userptr.c index d182058383a9ef..1719078c763ac0 100644 --- a/drivers/gpu/drm/i915/i915_gem_userptr.c +++ b/drivers/gpu/drm/i915/i915_gem_userptr.c @@ -113,7 +113,10 @@ static void *invalidate_range__linear(struct i915_mmu_notifier *mn, continue; obj = mo->obj; - drm_gem_object_reference(&obj->base); + + if (!kref_get_unless_zero(&obj->base.refcount)) + continue; + spin_unlock(&mn->lock); cancel_userptr(obj); @@ -149,7 +152,20 @@ static void i915_gem_userptr_mn_invalidate_range_start(struct mmu_notifier *_mn, it = interval_tree_iter_first(&mn->objects, start, end); if (it != NULL) { obj = container_of(it, struct i915_mmu_object, it)->obj; - drm_gem_object_reference(&obj->base); + + /* The mmu_object is released late when destroying the + * GEM object so it is entirely possible to gain a + * reference on an object in the process of being freed + * since our serialisation is via the spinlock and not + * the struct_mutex - and consequently use it after it + * is freed and then double free it. + */ + if (!kref_get_unless_zero(&obj->base.refcount)) { + spin_unlock(&mn->lock); + serial = 0; + continue; + } + serial = mn->serial; } spin_unlock(&mn->lock); From d94ef0b654606fdf6256d1cb2c9f0a2615f4ef04 Mon Sep 17 00:00:00 2001 From: Shobhit Kumar Date: Thu, 5 Feb 2015 17:10:56 +0530 Subject: [PATCH 234/788] drm/i915: Correct the IOSF Dev_FN field for IOSF transfers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit d180d2bbb66579e3bf449642b8ec2a76f4014fcd upstream. As per the specififcation, the SB_DevFn is the PCI_DEVFN of the target device and not the source. So PCI_DEVFN(2,0) is not correct. Further the port ID should be enough to identify devices unless they are MFD. The SB_DevFn was intended to remove ambiguity in case of these MFD devices. For non MFD devices the recommendation for the target device IP was to ignore these fields, but not all of them followed the recommendation. Some like CCK ignore these fields and hence PCI_DEVFN(2, 0) works and so does PCI_DEVFN(0, 0) as it works for DPIO. The issue came to light because of GPIONC which was not getting programmed correctly with PCI_DEVFN(2, 0). It turned out that this did not follow the recommendation and expected 0 in this field. In general the recommendation is to use SB_DevFn as PCI_DEVFN(0, 0) for all devices except target PCI devices. Signed-off-by: Shobhit Kumar Reviewed-by: Ville Syrjälä Signed-off-by: Jani Nikula Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/intel_sideband.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_sideband.c b/drivers/gpu/drm/i915/intel_sideband.c index 01d841ea3140f7..731b10a09aa098 100644 --- a/drivers/gpu/drm/i915/intel_sideband.c +++ b/drivers/gpu/drm/i915/intel_sideband.c @@ -82,7 +82,7 @@ u32 vlv_punit_read(struct drm_i915_private *dev_priv, u8 addr) WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); mutex_lock(&dev_priv->dpio_lock); - vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_PUNIT, + vlv_sideband_rw(dev_priv, PCI_DEVFN(0, 0), IOSF_PORT_PUNIT, SB_CRRDDA_NP, addr, &val); mutex_unlock(&dev_priv->dpio_lock); @@ -94,7 +94,7 @@ void vlv_punit_write(struct drm_i915_private *dev_priv, u8 addr, u32 val) WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); mutex_lock(&dev_priv->dpio_lock); - vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_PUNIT, + vlv_sideband_rw(dev_priv, PCI_DEVFN(0, 0), IOSF_PORT_PUNIT, SB_CRWRDA_NP, addr, &val); mutex_unlock(&dev_priv->dpio_lock); } @@ -103,7 +103,7 @@ u32 vlv_bunit_read(struct drm_i915_private *dev_priv, u32 reg) { u32 val = 0; - vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_BUNIT, + vlv_sideband_rw(dev_priv, PCI_DEVFN(0, 0), IOSF_PORT_BUNIT, SB_CRRDDA_NP, reg, &val); return val; @@ -111,7 +111,7 @@ u32 vlv_bunit_read(struct drm_i915_private *dev_priv, u32 reg) void vlv_bunit_write(struct drm_i915_private *dev_priv, u32 reg, u32 val) { - vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_BUNIT, + vlv_sideband_rw(dev_priv, PCI_DEVFN(0, 0), IOSF_PORT_BUNIT, SB_CRWRDA_NP, reg, &val); } @@ -122,7 +122,7 @@ u32 vlv_nc_read(struct drm_i915_private *dev_priv, u8 addr) WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); mutex_lock(&dev_priv->dpio_lock); - vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_NC, + vlv_sideband_rw(dev_priv, PCI_DEVFN(0, 0), IOSF_PORT_NC, SB_CRRDDA_NP, addr, &val); mutex_unlock(&dev_priv->dpio_lock); @@ -132,56 +132,56 @@ u32 vlv_nc_read(struct drm_i915_private *dev_priv, u8 addr) u32 vlv_gpio_nc_read(struct drm_i915_private *dev_priv, u32 reg) { u32 val = 0; - vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_GPIO_NC, + vlv_sideband_rw(dev_priv, PCI_DEVFN(0, 0), IOSF_PORT_GPIO_NC, SB_CRRDDA_NP, reg, &val); return val; } void vlv_gpio_nc_write(struct drm_i915_private *dev_priv, u32 reg, u32 val) { - vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_GPIO_NC, + vlv_sideband_rw(dev_priv, PCI_DEVFN(0, 0), IOSF_PORT_GPIO_NC, SB_CRWRDA_NP, reg, &val); } u32 vlv_cck_read(struct drm_i915_private *dev_priv, u32 reg) { u32 val = 0; - vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_CCK, + vlv_sideband_rw(dev_priv, PCI_DEVFN(0, 0), IOSF_PORT_CCK, SB_CRRDDA_NP, reg, &val); return val; } void vlv_cck_write(struct drm_i915_private *dev_priv, u32 reg, u32 val) { - vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_CCK, + vlv_sideband_rw(dev_priv, PCI_DEVFN(0, 0), IOSF_PORT_CCK, SB_CRWRDA_NP, reg, &val); } u32 vlv_ccu_read(struct drm_i915_private *dev_priv, u32 reg) { u32 val = 0; - vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_CCU, + vlv_sideband_rw(dev_priv, PCI_DEVFN(0, 0), IOSF_PORT_CCU, SB_CRRDDA_NP, reg, &val); return val; } void vlv_ccu_write(struct drm_i915_private *dev_priv, u32 reg, u32 val) { - vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_CCU, + vlv_sideband_rw(dev_priv, PCI_DEVFN(0, 0), IOSF_PORT_CCU, SB_CRWRDA_NP, reg, &val); } u32 vlv_gps_core_read(struct drm_i915_private *dev_priv, u32 reg) { u32 val = 0; - vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_GPS_CORE, + vlv_sideband_rw(dev_priv, PCI_DEVFN(0, 0), IOSF_PORT_GPS_CORE, SB_CRRDDA_NP, reg, &val); return val; } void vlv_gps_core_write(struct drm_i915_private *dev_priv, u32 reg, u32 val) { - vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_GPS_CORE, + vlv_sideband_rw(dev_priv, PCI_DEVFN(0, 0), IOSF_PORT_GPS_CORE, SB_CRWRDA_NP, reg, &val); } From a88c8685cd4acacc9bd6234e4f8124ec1ce7e494 Mon Sep 17 00:00:00 2001 From: Tom O'Rourke Date: Tue, 10 Feb 2015 23:06:46 -0800 Subject: [PATCH 235/788] drm/i915: Clamp efficient frequency to valid range commit 46efa4abe5712276494adbce102f46e3214632fd upstream. The efficient frequency (RPe) should stay in the range RPn <= RPe <= RP0. The pcode clamps the returned value internally on Broadwell but not on Haswell. Fix for missing range check in commit 93ee29203f506582cca2bcec5f05041526d9ab0a Author: Tom O'Rourke Date: Wed Nov 19 14:21:52 2014 -0800 drm/i915: Use efficient frequency for HSW/BDW Reference: http://lists.freedesktop.org/archives/intel-gfx/2015-February/059802.html Reported-by: Michael Auchter Suggested-by: Chris Wilson Signed-off-by: Tom O'Rourke Signed-off-by: Jani Nikula Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/intel_pm.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index bf814a64582a3e..e6d3593719712a 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4625,7 +4625,10 @@ static void gen6_init_rps_frequencies(struct drm_device *dev) &ddcc_status); if (0 == ret) dev_priv->rps.efficient_freq = - (ddcc_status >> 8) & 0xff; + clamp_t(u8, + ((ddcc_status >> 8) & 0xff), + dev_priv->rps.min_freq, + dev_priv->rps.max_freq); } /* Preserve min/max settings in case of re-init */ From 12c5ac27e69ca3d652a95d3f99423e5fadb274a1 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Wed, 11 Feb 2015 18:34:40 -0800 Subject: [PATCH 236/788] target: Fix PR_APTPL_BUF_LEN buffer size limitation commit f161d4b44d7cc1dc66b53365215227db356378b1 upstream. This patch addresses the original PR_APTPL_BUF_LEN = 8k limitiation for write-out of PR APTPL metadata that Martin has recently been running into. It changes core_scsi3_update_and_write_aptpl() to use vzalloc'ed memory instead of kzalloc, and increases the default hardcoded length to 256k. It also adds logic in core_scsi3_update_and_write_aptpl() to double the original length upon core_scsi3_update_aptpl_buf() failure, and retries until the vzalloc'ed buffer is large enough to accommodate the outgoing APTPL metadata. Reported-by: Martin Svec Signed-off-by: Nicholas Bellinger Signed-off-by: Greg Kroah-Hartman --- drivers/target/target_core_pr.c | 25 +++++++++++++------------ include/target/target_core_base.h | 2 +- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index 283cf786ef98be..2de6fb8cee8d83 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -1874,8 +1874,8 @@ static int core_scsi3_update_aptpl_buf( } if ((len + strlen(tmp) >= pr_aptpl_buf_len)) { - pr_err("Unable to update renaming" - " APTPL metadata\n"); + pr_err("Unable to update renaming APTPL metadata," + " reallocating larger buffer\n"); ret = -EMSGSIZE; goto out; } @@ -1892,8 +1892,8 @@ static int core_scsi3_update_aptpl_buf( lun->lun_sep->sep_rtpi, lun->unpacked_lun, reg_count); if ((len + strlen(tmp) >= pr_aptpl_buf_len)) { - pr_err("Unable to update renaming" - " APTPL metadata\n"); + pr_err("Unable to update renaming APTPL metadata," + " reallocating larger buffer\n"); ret = -EMSGSIZE; goto out; } @@ -1956,7 +1956,7 @@ static int __core_scsi3_write_aptpl_to_file( static sense_reason_t core_scsi3_update_and_write_aptpl(struct se_device *dev, bool aptpl) { unsigned char *buf; - int rc; + int rc, len = PR_APTPL_BUF_LEN; if (!aptpl) { char *null_buf = "No Registrations or Reservations\n"; @@ -1970,25 +1970,26 @@ static sense_reason_t core_scsi3_update_and_write_aptpl(struct se_device *dev, b return 0; } - - buf = kzalloc(PR_APTPL_BUF_LEN, GFP_KERNEL); +retry: + buf = vzalloc(len); if (!buf) return TCM_OUT_OF_RESOURCES; - rc = core_scsi3_update_aptpl_buf(dev, buf, PR_APTPL_BUF_LEN); + rc = core_scsi3_update_aptpl_buf(dev, buf, len); if (rc < 0) { - kfree(buf); - return TCM_OUT_OF_RESOURCES; + vfree(buf); + len *= 2; + goto retry; } rc = __core_scsi3_write_aptpl_to_file(dev, buf); if (rc != 0) { pr_err("SPC-3 PR: Could not update APTPL\n"); - kfree(buf); + vfree(buf); return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; } dev->t10_pr.pr_aptpl_active = 1; - kfree(buf); + vfree(buf); pr_debug("SPC-3 PR: Set APTPL Bit Activated\n"); return 0; } diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index 4a8795a87b9e99..672150b6aaf52b 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -407,7 +407,7 @@ struct t10_reservation { /* Activate Persistence across Target Power Loss enabled * for SCSI device */ int pr_aptpl_active; -#define PR_APTPL_BUF_LEN 8192 +#define PR_APTPL_BUF_LEN 262144 u32 pr_generation; spinlock_t registration_lock; spinlock_t aptpl_reg_lock; From 1a36e39ae78ce0aabbe4932d152d83b9352d97ae Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Fri, 13 Feb 2015 22:09:47 +0000 Subject: [PATCH 237/788] target: Add missing WRITE_SAME end-of-device sanity check commit 8e575c50a171f2579e367a7f778f86477dfdaf49 upstream. This patch adds a check to sbc_setup_write_same() to verify the incoming WRITE_SAME LBA + number of blocks does not exceed past the end-of-device. Also check for potential LBA wrap-around as well. Reported-by: Bart Van Assche Cc: Martin Petersen Cc: Christoph Hellwig Signed-off-by: Nicholas Bellinger Signed-off-by: Greg Kroah-Hartman --- drivers/target/target_core_sbc.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index cd4bed7b27579b..9f1b9fb764e4f3 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -251,6 +251,8 @@ static inline unsigned long long transport_lba_64_ext(unsigned char *cdb) static sense_reason_t sbc_setup_write_same(struct se_cmd *cmd, unsigned char *flags, struct sbc_ops *ops) { + struct se_device *dev = cmd->se_dev; + sector_t end_lba = dev->transport->get_blocks(dev) + 1; unsigned int sectors = sbc_get_write_same_sectors(cmd); if ((flags[0] & 0x04) || (flags[0] & 0x02)) { @@ -264,6 +266,16 @@ sbc_setup_write_same(struct se_cmd *cmd, unsigned char *flags, struct sbc_ops *o sectors, cmd->se_dev->dev_attrib.max_write_same_len); return TCM_INVALID_CDB_FIELD; } + /* + * Sanity check for LBA wrap and request past end of device. + */ + if (((cmd->t_task_lba + sectors) < cmd->t_task_lba) || + ((cmd->t_task_lba + sectors) > end_lba)) { + pr_err("WRITE_SAME exceeds last lba %llu (lba %llu, sectors %u)\n", + (unsigned long long)end_lba, cmd->t_task_lba, sectors); + return TCM_ADDRESS_OUT_OF_RANGE; + } + /* We always have ANC_SUP == 0 so setting ANCHOR is always an error */ if (flags[0] & 0x10) { pr_warn("WRITE SAME with ANCHOR not supported\n"); From 5b69eac7c2fea68f6c53994915f8ad47f06f291e Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Fri, 13 Feb 2015 22:27:40 +0000 Subject: [PATCH 238/788] target: Check for LBA + sectors wrap-around in sbc_parse_cdb commit aa179935edea9a64dec4b757090c8106a3907ffa upstream. This patch adds a check to sbc_parse_cdb() in order to detect when an LBA + sector vs. end-of-device calculation wraps when the LBA is sufficently large enough (eg: 0xFFFFFFFFFFFFFFFF). Cc: Martin Petersen Cc: Christoph Hellwig Signed-off-by: Nicholas Bellinger Signed-off-by: Greg Kroah-Hartman --- drivers/target/target_core_sbc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index 9f1b9fb764e4f3..36b471389169f7 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -967,7 +967,8 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops) unsigned long long end_lba; check_lba: end_lba = dev->transport->get_blocks(dev) + 1; - if (cmd->t_task_lba + sectors > end_lba) { + if (((cmd->t_task_lba + sectors) < cmd->t_task_lba) || + ((cmd->t_task_lba + sectors) > end_lba)) { pr_err("cmd exceeds last lba %llu " "(lba %llu, sectors %u)\n", end_lba, cmd->t_task_lba, sectors); From 1f4d987805f78ddbc03cd901eed43aeb60ee344a Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Thu, 5 Mar 2015 01:09:44 +0100 Subject: [PATCH 239/788] x86/asm/entry/64: Remove a bogus 'ret_from_fork' optimization commit 956421fbb74c3a6261903f3836c0740187cf038b upstream. 'ret_from_fork' checks TIF_IA32 to determine whether 'pt_regs' and the related state make sense for 'ret_from_sys_call'. This is entirely the wrong check. TS_COMPAT would make a little more sense, but there's really no point in keeping this optimization at all. This fixes a return to the wrong user CS if we came from int 0x80 in a 64-bit task. Signed-off-by: Andy Lutomirski Cc: Borislav Petkov Cc: Denys Vlasenko Cc: H. Peter Anvin Cc: Linus Torvalds Cc: Oleg Nesterov Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/4710be56d76ef994ddf59087aad98c000fbab9a4.1424989793.git.luto@amacapital.net [ Backported from tip:x86/asm. ] Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/entry_64.S | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index 9ebaf63ba18212..4ee9a2315f80da 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S @@ -334,11 +334,14 @@ ENTRY(ret_from_fork) testl $3, CS-ARGOFFSET(%rsp) # from kernel_thread? jz 1f - testl $_TIF_IA32, TI_flags(%rcx) # 32-bit compat task needs IRET - jnz int_ret_from_sys_call - - RESTORE_TOP_OF_STACK %rdi, -ARGOFFSET - jmp ret_from_sys_call # go to the SYSRET fastpath + /* + * By the time we get here, we have no idea whether our pt_regs, + * ti flags, and ti status came from the 64-bit SYSCALL fast path, + * the slow path, or one of the ia32entry paths. + * Use int_ret_from_sys_call to return, since it can safely handle + * all of the above. + */ + jmp int_ret_from_sys_call 1: subq $REST_SKIP, %rsp # leave space for volatiles From 8b6055fc682c2cbbdca5ff3a71af663af986f0e6 Mon Sep 17 00:00:00 2001 From: Quentin Casasnovas Date: Thu, 5 Mar 2015 13:19:22 +0100 Subject: [PATCH 240/788] x86/fpu/xsaves: Fix improper uses of __ex_table commit 06c8173eb92bbfc03a0fe8bb64315857d0badd06 upstream. Commit: f31a9f7c7169 ("x86/xsaves: Use xsaves/xrstors to save and restore xsave area") introduced alternative instructions for XSAVES/XRSTORS and commit: adb9d526e982 ("x86/xsaves: Add xsaves and xrstors support for booting time") added support for the XSAVES/XRSTORS instructions at boot time. Unfortunately both failed to properly protect them against faulting: The 'xstate_fault' macro will use the closest label named '1' backward and that ends up in the .altinstr_replacement section rather than in .text. This means that the kernel will never find in the __ex_table the .text address where this instruction might fault, leading to serious problems if userspace manages to trigger the fault. Signed-off-by: Quentin Casasnovas Signed-off-by: Jamie Iles [ Improved the changelog, fixed some whitespace noise. ] Acked-by: Borislav Petkov Acked-by: Linus Torvalds Cc: Allan Xavier Cc: H. Peter Anvin Cc: Thomas Gleixner Fixes: adb9d526e982 ("x86/xsaves: Add xsaves and xrstors support for booting time") Fixes: f31a9f7c7169 ("x86/xsaves: Use xsaves/xrstors to save and restore xsave area") Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/xsave.h | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/arch/x86/include/asm/xsave.h b/arch/x86/include/asm/xsave.h index 5fa9770035dc93..c9a6d68b8d623b 100644 --- a/arch/x86/include/asm/xsave.h +++ b/arch/x86/include/asm/xsave.h @@ -82,18 +82,15 @@ static inline int xsave_state_booting(struct xsave_struct *fx, u64 mask) if (boot_cpu_has(X86_FEATURE_XSAVES)) asm volatile("1:"XSAVES"\n\t" "2:\n\t" - : : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask) + xstate_fault + : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask) : "memory"); else asm volatile("1:"XSAVE"\n\t" "2:\n\t" - : : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask) + xstate_fault + : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask) : "memory"); - - asm volatile(xstate_fault - : "0" (0) - : "memory"); - return err; } @@ -112,18 +109,15 @@ static inline int xrstor_state_booting(struct xsave_struct *fx, u64 mask) if (boot_cpu_has(X86_FEATURE_XSAVES)) asm volatile("1:"XRSTORS"\n\t" "2:\n\t" - : : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask) + xstate_fault + : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask) : "memory"); else asm volatile("1:"XRSTOR"\n\t" "2:\n\t" - : : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask) + xstate_fault + : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask) : "memory"); - - asm volatile(xstate_fault - : "0" (0) - : "memory"); - return err; } @@ -149,9 +143,9 @@ static inline int xsave_state(struct xsave_struct *fx, u64 mask) */ alternative_input_2( "1:"XSAVE, - "1:"XSAVEOPT, + XSAVEOPT, X86_FEATURE_XSAVEOPT, - "1:"XSAVES, + XSAVES, X86_FEATURE_XSAVES, [fx] "D" (fx), "a" (lmask), "d" (hmask) : "memory"); @@ -178,7 +172,7 @@ static inline int xrstor_state(struct xsave_struct *fx, u64 mask) */ alternative_input( "1: " XRSTOR, - "1: " XRSTORS, + XRSTORS, X86_FEATURE_XSAVES, "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask) : "memory"); From 569a32a34f892185d39d08f30bf8c90a531db6a8 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Sat, 3 Jan 2015 20:34:12 +0000 Subject: [PATCH 241/788] iio: mxs-lradc: fix iio channel map regression commit 03305e535cd5cdc1079b32909bf4b2dd67d46f7f upstream. Since commit c8231a9af8147f8a ("iio: mxs-lradc: compute temperature from channel 8 and 9") with the removal of adc channel 9 there is no 1-1 mapping in the channel spec. All hwmon channel values above 9 are accessible via there index minus one. So add a hidden iio channel 9 to fix this issue. Signed-off-by: Stefan Wahren Acked-by: Alexandre Belloni Reviewed-by: Marek Vasut Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/adc/mxs-lradc.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/staging/iio/adc/mxs-lradc.c b/drivers/staging/iio/adc/mxs-lradc.c index f053535385bf94..1da2411ff09402 100644 --- a/drivers/staging/iio/adc/mxs-lradc.c +++ b/drivers/staging/iio/adc/mxs-lradc.c @@ -1406,6 +1406,13 @@ static const struct iio_chan_spec mxs_lradc_chan_spec[] = { .channel = 8, .scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,}, }, + /* Hidden channel to keep indexes */ + { + .type = IIO_TEMP, + .indexed = 1, + .scan_index = -1, + .channel = 9, + }, MXS_ADC_CHAN(10, IIO_VOLTAGE), /* VDDIO */ MXS_ADC_CHAN(11, IIO_VOLTAGE), /* VTH */ MXS_ADC_CHAN(12, IIO_VOLTAGE), /* VDDA */ From 6c4edc63f0fde2267b236b6d533da96aefb95e4e Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Fri, 23 Jan 2015 00:34:02 +0100 Subject: [PATCH 242/788] iio: imu: adis16400: Fix sign extension commit 19e353f2b344ad86cea6ebbc0002e5f903480a90 upstream. The intention is obviously to sign-extend a 12 bit quantity. But because of C's promotion rules, the assignment is equivalent to "val16 &= 0xfff;". Use the proper API for this. Signed-off-by: Rasmus Villemoes Acked-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/imu/adis16400_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/imu/adis16400_core.c b/drivers/iio/imu/adis16400_core.c index b70873de04ea50..fa795dcd5f75ec 100644 --- a/drivers/iio/imu/adis16400_core.c +++ b/drivers/iio/imu/adis16400_core.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -414,7 +415,7 @@ static int adis16400_read_raw(struct iio_dev *indio_dev, mutex_unlock(&indio_dev->mlock); if (ret) return ret; - val16 = ((val16 & 0xFFF) << 4) >> 4; + val16 = sign_extend32(val16, 11); *val = val16; return IIO_VAL_INT; case IIO_CHAN_INFO_OFFSET: From ec43713668e5c247eda1d6a829a59879f4068619 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristina=20Mart=C5=A1enko?= Date: Sun, 25 Jan 2015 18:28:19 +0200 Subject: [PATCH 243/788] iio: mxs-lradc: separate touchscreen and buffer virtual channels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit f81197b8a31b8fb287ae57f597b5b6841e1ece92 upstream. The touchscreen was initially designed [1] to map all of its physical channels to one virtual channel, leaving buffered capture to use the remaining 7 virtual channels. When the touchscreen was reimplemented [2], it was made to use four virtual channels, which overlap and conflict with the channels the buffer uses. As a result, when the buffer is enabled, the touchscreen's virtual channels are remapped to whichever physical channels the buffer was configured with, causing the touchscreen to read those instead of the touch measurement channels. Effectively the touchscreen stops working. So here we separate the channels again, giving the touchscreen 2 virtual channels and the buffer 6. We can't give the touchscreen just 1 channel as before, as the current pressure calculation requires 2 channels to be read at the same time. This makes the touchscreen continue to work during buffered capture. It has been tested on i.MX28, but not on i.MX23. [1] 06ddd353f5c8 ("iio: mxs: Implement support for touchscreen") [2] dee05308f602 ("Staging/iio/adc/touchscreen/MXS: add interrupt driven touch detection") Signed-off-by: Kristina Martšenko Reviewed-by: Marek Vasut Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/adc/mxs-lradc.c | 166 +++++++++++++--------------- 1 file changed, 75 insertions(+), 91 deletions(-) diff --git a/drivers/staging/iio/adc/mxs-lradc.c b/drivers/staging/iio/adc/mxs-lradc.c index 1da2411ff09402..cce3cb2ee26ebd 100644 --- a/drivers/staging/iio/adc/mxs-lradc.c +++ b/drivers/staging/iio/adc/mxs-lradc.c @@ -214,11 +214,14 @@ struct mxs_lradc { unsigned long is_divided; /* - * Touchscreen LRADC channels receives a private slot in the CTRL4 - * register, the slot #7. Therefore only 7 slots instead of 8 in the - * CTRL4 register can be mapped to LRADC channels when using the - * touchscreen. - * + * When the touchscreen is enabled, we give it two private virtual + * channels: #6 and #7. This means that only 6 virtual channels (instead + * of 8) will be available for buffered capture. + */ +#define TOUCHSCREEN_VCHANNEL1 7 +#define TOUCHSCREEN_VCHANNEL2 6 + + /* * Furthermore, certain LRADC channels are shared between touchscreen * and/or touch-buttons and generic LRADC block. Therefore when using * either of these, these channels are not available for the regular @@ -342,6 +345,9 @@ struct mxs_lradc { #define LRADC_CTRL4 0x140 #define LRADC_CTRL4_LRADCSELECT_MASK(n) (0xf << ((n) * 4)) #define LRADC_CTRL4_LRADCSELECT_OFFSET(n) ((n) * 4) +#define LRADC_CTRL4_LRADCSELECT(n, x) \ + (((x) << LRADC_CTRL4_LRADCSELECT_OFFSET(n)) & \ + LRADC_CTRL4_LRADCSELECT_MASK(n)) #define LRADC_RESOLUTION 12 #define LRADC_SINGLE_SAMPLE_MASK ((1 << LRADC_RESOLUTION) - 1) @@ -416,6 +422,14 @@ static bool mxs_lradc_check_touch_event(struct mxs_lradc *lradc) LRADC_STATUS_TOUCH_DETECT_RAW); } +static void mxs_lradc_map_channel(struct mxs_lradc *lradc, unsigned vch, + unsigned ch) +{ + mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(vch), + LRADC_CTRL4); + mxs_lradc_reg_set(lradc, LRADC_CTRL4_LRADCSELECT(vch, ch), LRADC_CTRL4); +} + static void mxs_lradc_setup_ts_channel(struct mxs_lradc *lradc, unsigned ch) { /* @@ -443,12 +457,8 @@ static void mxs_lradc_setup_ts_channel(struct mxs_lradc *lradc, unsigned ch) LRADC_DELAY_DELAY(lradc->over_sample_delay - 1), LRADC_DELAY(3)); - mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(2) | - LRADC_CTRL1_LRADC_IRQ(3) | LRADC_CTRL1_LRADC_IRQ(4) | - LRADC_CTRL1_LRADC_IRQ(5), LRADC_CTRL1); + mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch), LRADC_CTRL1); - /* wake us again, when the complete conversion is done */ - mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(ch), LRADC_CTRL1); /* * after changing the touchscreen plates setting * the signals need some initial time to settle. Start the @@ -502,12 +512,8 @@ static void mxs_lradc_setup_ts_pressure(struct mxs_lradc *lradc, unsigned ch1, LRADC_DELAY_DELAY(lradc->over_sample_delay - 1), LRADC_DELAY(3)); - mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(2) | - LRADC_CTRL1_LRADC_IRQ(3) | LRADC_CTRL1_LRADC_IRQ(4) | - LRADC_CTRL1_LRADC_IRQ(5), LRADC_CTRL1); + mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch2), LRADC_CTRL1); - /* wake us again, when the conversions are done */ - mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(ch2), LRADC_CTRL1); /* * after changing the touchscreen plates setting * the signals need some initial time to settle. Start the @@ -573,36 +579,6 @@ static unsigned mxs_lradc_read_ts_pressure(struct mxs_lradc *lradc, #define TS_CH_XM 4 #define TS_CH_YM 5 -static int mxs_lradc_read_ts_channel(struct mxs_lradc *lradc) -{ - u32 reg; - int val; - - reg = readl(lradc->base + LRADC_CTRL1); - - /* only channels 3 to 5 are of interest here */ - if (reg & LRADC_CTRL1_LRADC_IRQ(TS_CH_YP)) { - mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(TS_CH_YP) | - LRADC_CTRL1_LRADC_IRQ(TS_CH_YP), LRADC_CTRL1); - val = mxs_lradc_read_raw_channel(lradc, TS_CH_YP); - } else if (reg & LRADC_CTRL1_LRADC_IRQ(TS_CH_XM)) { - mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(TS_CH_XM) | - LRADC_CTRL1_LRADC_IRQ(TS_CH_XM), LRADC_CTRL1); - val = mxs_lradc_read_raw_channel(lradc, TS_CH_XM); - } else if (reg & LRADC_CTRL1_LRADC_IRQ(TS_CH_YM)) { - mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(TS_CH_YM) | - LRADC_CTRL1_LRADC_IRQ(TS_CH_YM), LRADC_CTRL1); - val = mxs_lradc_read_raw_channel(lradc, TS_CH_YM); - } else { - return -EIO; - } - - mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2)); - mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3)); - - return val; -} - /* * YP(open)--+-------------+ * | |--+ @@ -646,7 +622,8 @@ static void mxs_lradc_prepare_x_pos(struct mxs_lradc *lradc) mxs_lradc_reg_set(lradc, mxs_lradc_drive_x_plate(lradc), LRADC_CTRL0); lradc->cur_plate = LRADC_SAMPLE_X; - mxs_lradc_setup_ts_channel(lradc, TS_CH_YP); + mxs_lradc_map_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YP); + mxs_lradc_setup_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1); } /* @@ -667,7 +644,8 @@ static void mxs_lradc_prepare_y_pos(struct mxs_lradc *lradc) mxs_lradc_reg_set(lradc, mxs_lradc_drive_y_plate(lradc), LRADC_CTRL0); lradc->cur_plate = LRADC_SAMPLE_Y; - mxs_lradc_setup_ts_channel(lradc, TS_CH_XM); + mxs_lradc_map_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_XM); + mxs_lradc_setup_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1); } /* @@ -688,7 +666,10 @@ static void mxs_lradc_prepare_pressure(struct mxs_lradc *lradc) mxs_lradc_reg_set(lradc, mxs_lradc_drive_pressure(lradc), LRADC_CTRL0); lradc->cur_plate = LRADC_SAMPLE_PRESSURE; - mxs_lradc_setup_ts_pressure(lradc, TS_CH_XP, TS_CH_YM); + mxs_lradc_map_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YM); + mxs_lradc_map_channel(lradc, TOUCHSCREEN_VCHANNEL2, TS_CH_XP); + mxs_lradc_setup_ts_pressure(lradc, TOUCHSCREEN_VCHANNEL2, + TOUCHSCREEN_VCHANNEL1); } static void mxs_lradc_enable_touch_detection(struct mxs_lradc *lradc) @@ -701,6 +682,19 @@ static void mxs_lradc_enable_touch_detection(struct mxs_lradc *lradc) mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1); } +static void mxs_lradc_start_touch_event(struct mxs_lradc *lradc) +{ + mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, + LRADC_CTRL1); + mxs_lradc_reg_set(lradc, + LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1), LRADC_CTRL1); + /* + * start with the Y-pos, because it uses nearly the same plate + * settings like the touch detection + */ + mxs_lradc_prepare_y_pos(lradc); +} + static void mxs_lradc_report_ts_event(struct mxs_lradc *lradc) { input_report_abs(lradc->ts_input, ABS_X, lradc->ts_x_pos); @@ -718,10 +712,12 @@ static void mxs_lradc_complete_touch_event(struct mxs_lradc *lradc) * start a dummy conversion to burn time to settle the signals * note: we are not interested in the conversion's value */ - mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(5)); - mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(5), LRADC_CTRL1); - mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(5), LRADC_CTRL1); - mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(1 << 5) | + mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(TOUCHSCREEN_VCHANNEL1)); + mxs_lradc_reg_clear(lradc, + LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) | + LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2), LRADC_CTRL1); + mxs_lradc_reg_wrt(lradc, + LRADC_DELAY_TRIGGER(1 << TOUCHSCREEN_VCHANNEL1) | LRADC_DELAY_KICK | LRADC_DELAY_DELAY(10), /* waste 5 ms */ LRADC_DELAY(2)); } @@ -753,59 +749,45 @@ static void mxs_lradc_finish_touch_event(struct mxs_lradc *lradc, bool valid) /* if it is released, wait for the next touch via IRQ */ lradc->cur_plate = LRADC_TOUCH; - mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ, LRADC_CTRL1); + mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2)); + mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3)); + mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ | + LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) | + LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1), LRADC_CTRL1); mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1); } /* touchscreen's state machine */ static void mxs_lradc_handle_touch(struct mxs_lradc *lradc) { - int val; - switch (lradc->cur_plate) { case LRADC_TOUCH: - /* - * start with the Y-pos, because it uses nearly the same plate - * settings like the touch detection - */ - if (mxs_lradc_check_touch_event(lradc)) { - mxs_lradc_reg_clear(lradc, - LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, - LRADC_CTRL1); - mxs_lradc_prepare_y_pos(lradc); - } + if (mxs_lradc_check_touch_event(lradc)) + mxs_lradc_start_touch_event(lradc); mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ, LRADC_CTRL1); return; case LRADC_SAMPLE_Y: - val = mxs_lradc_read_ts_channel(lradc); - if (val < 0) { - mxs_lradc_enable_touch_detection(lradc); /* re-start */ - return; - } - lradc->ts_y_pos = val; + lradc->ts_y_pos = mxs_lradc_read_raw_channel(lradc, + TOUCHSCREEN_VCHANNEL1); mxs_lradc_prepare_x_pos(lradc); return; case LRADC_SAMPLE_X: - val = mxs_lradc_read_ts_channel(lradc); - if (val < 0) { - mxs_lradc_enable_touch_detection(lradc); /* re-start */ - return; - } - lradc->ts_x_pos = val; + lradc->ts_x_pos = mxs_lradc_read_raw_channel(lradc, + TOUCHSCREEN_VCHANNEL1); mxs_lradc_prepare_pressure(lradc); return; case LRADC_SAMPLE_PRESSURE: - lradc->ts_pressure = - mxs_lradc_read_ts_pressure(lradc, TS_CH_XP, TS_CH_YM); + lradc->ts_pressure = mxs_lradc_read_ts_pressure(lradc, + TOUCHSCREEN_VCHANNEL2, + TOUCHSCREEN_VCHANNEL1); mxs_lradc_complete_touch_event(lradc); return; case LRADC_SAMPLE_VALID: - val = mxs_lradc_read_ts_channel(lradc); /* ignore the value */ mxs_lradc_finish_touch_event(lradc, 1); break; } @@ -1083,9 +1065,8 @@ static void mxs_lradc_disable_ts(struct mxs_lradc *lradc) { /* stop all interrupts from firing */ mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN | - LRADC_CTRL1_LRADC_IRQ_EN(2) | LRADC_CTRL1_LRADC_IRQ_EN(3) | - LRADC_CTRL1_LRADC_IRQ_EN(4) | LRADC_CTRL1_LRADC_IRQ_EN(5), - LRADC_CTRL1); + LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) | + LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL2), LRADC_CTRL1); /* Power-down touchscreen touch-detect circuitry. */ mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0); @@ -1151,26 +1132,29 @@ static irqreturn_t mxs_lradc_handle_irq(int irq, void *data) struct iio_dev *iio = data; struct mxs_lradc *lradc = iio_priv(iio); unsigned long reg = readl(lradc->base + LRADC_CTRL1); + uint32_t clr_irq = mxs_lradc_irq_mask(lradc); const uint32_t ts_irq_mask = LRADC_CTRL1_TOUCH_DETECT_IRQ | - LRADC_CTRL1_LRADC_IRQ(2) | - LRADC_CTRL1_LRADC_IRQ(3) | - LRADC_CTRL1_LRADC_IRQ(4) | - LRADC_CTRL1_LRADC_IRQ(5); + LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) | + LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2); if (!(reg & mxs_lradc_irq_mask(lradc))) return IRQ_NONE; - if (lradc->use_touchscreen && (reg & ts_irq_mask)) + if (lradc->use_touchscreen && (reg & ts_irq_mask)) { mxs_lradc_handle_touch(lradc); + /* Make sure we don't clear the next conversion's interrupt. */ + clr_irq &= ~(LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) | + LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2)); + } + if (iio_buffer_enabled(iio)) iio_trigger_poll(iio->trig); else if (reg & LRADC_CTRL1_LRADC_IRQ(0)) complete(&lradc->completion); - mxs_lradc_reg_clear(lradc, reg & mxs_lradc_irq_mask(lradc), - LRADC_CTRL1); + mxs_lradc_reg_clear(lradc, reg & clr_irq, LRADC_CTRL1); return IRQ_HANDLED; } @@ -1346,7 +1330,7 @@ static bool mxs_lradc_validate_scan_mask(struct iio_dev *iio, if (lradc->use_touchbutton) rsvd_chans++; if (lradc->use_touchscreen) - rsvd_chans++; + rsvd_chans += 2; /* Test for attempts to map channels with special mode of operation. */ if (bitmap_intersects(mask, &rsvd_mask, LRADC_MAX_TOTAL_CHANS)) From 119b8ee8826d5352589e3ecbafd3ff05d0ec3cdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristina=20Mart=C5=A1enko?= Date: Sun, 25 Jan 2015 18:28:20 +0200 Subject: [PATCH 244/788] iio: mxs-lradc: make ADC reads not disable touchscreen interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 86bf7f3ef7e961e91e16dceb31ae0f583483b204 upstream. Reading a channel through sysfs, or starting a buffered capture, will currently turn off the touchscreen. This is because the read_raw() and buffer preenable()/postdisable() callbacks disable interrupts for all LRADC channels, including those the touchscreen uses. So make the callbacks only disable interrupts for the channels they use. This means channel 0 for read_raw() and channels 0-5 for the buffer (if the touchscreen is enabled). Since the touchscreen uses different channels (6 and 7), it no longer gets turned off. Note that only i.MX28 is affected by this issue, i.MX23 should be fine. Signed-off-by: Kristina Martšenko Reviewed-by: Marek Vasut Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/adc/mxs-lradc.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/staging/iio/adc/mxs-lradc.c b/drivers/staging/iio/adc/mxs-lradc.c index cce3cb2ee26ebd..9a9b1a73aa8690 100644 --- a/drivers/staging/iio/adc/mxs-lradc.c +++ b/drivers/staging/iio/adc/mxs-lradc.c @@ -220,6 +220,9 @@ struct mxs_lradc { */ #define TOUCHSCREEN_VCHANNEL1 7 #define TOUCHSCREEN_VCHANNEL2 6 +#define BUFFER_VCHANS_LIMITED 0x3f +#define BUFFER_VCHANS_ALL 0xff + u8 buffer_vchans; /* * Furthermore, certain LRADC channels are shared between touchscreen @@ -819,7 +822,7 @@ static int mxs_lradc_read_single(struct iio_dev *iio_dev, int chan, int *val) * used if doing raw sampling. */ if (lradc->soc == IMX28_LRADC) - mxs_lradc_reg_clear(lradc, LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK, + mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0), LRADC_CTRL1); mxs_lradc_reg_clear(lradc, 0xff, LRADC_CTRL0); @@ -1266,8 +1269,9 @@ static int mxs_lradc_buffer_preenable(struct iio_dev *iio) } if (lradc->soc == IMX28_LRADC) - mxs_lradc_reg_clear(lradc, LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK, - LRADC_CTRL1); + mxs_lradc_reg_clear(lradc, + lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET, + LRADC_CTRL1); mxs_lradc_reg_clear(lradc, 0xff, LRADC_CTRL0); for_each_set_bit(chan, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) { @@ -1303,8 +1307,9 @@ static int mxs_lradc_buffer_postdisable(struct iio_dev *iio) mxs_lradc_reg_clear(lradc, 0xff, LRADC_CTRL0); if (lradc->soc == IMX28_LRADC) - mxs_lradc_reg_clear(lradc, LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK, - LRADC_CTRL1); + mxs_lradc_reg_clear(lradc, + lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET, + LRADC_CTRL1); kfree(lradc->buffer); mutex_unlock(&lradc->lock); @@ -1549,6 +1554,11 @@ static int mxs_lradc_probe(struct platform_device *pdev) touch_ret = mxs_lradc_probe_touchscreen(lradc, node); + if (touch_ret == 0) + lradc->buffer_vchans = BUFFER_VCHANS_LIMITED; + else + lradc->buffer_vchans = BUFFER_VCHANS_ALL; + /* Grab all IRQ sources */ for (i = 0; i < of_cfg->irq_count; i++) { lradc->irq[i] = platform_get_irq(pdev, i); From 2b55aacbd93ed7117ee1fb17dba974fe685dc11e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristina=20Mart=C5=A1enko?= Date: Sun, 25 Jan 2015 18:28:21 +0200 Subject: [PATCH 245/788] iio: mxs-lradc: make ADC reads not unschedule touchscreen conversions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 6abe0300a1d5242f4ff89257197f284679af1a06 upstream. Reading a channel through sysfs, or starting a buffered capture, can occasionally turn off the touchscreen. This is because the read_raw() and buffer preenable()/postdisable() callbacks unschedule current conversions on all channels. If a delay channel happens to schedule a touchscreen conversion at the same time, the conversion gets cancelled and the touchscreen sequence stops. This is probably related to this note from the reference manual: "If a delay group schedules channels to be sampled and a manual write to the schedule field in CTRL0 occurs while the block is discarding samples, the LRADC will switch to the new schedule and will not sample the channels that were previously scheduled. The time window for this to happen is very small and lasts only while the LRADC is discarding samples." So make the callbacks only unschedule conversions for the channels they use. This means channel 0 for read_raw() and channels 0-5 for the buffer (if the touchscreen is enabled). Since the touchscreen uses different channels (6 and 7), it no longer gets turned off. This is tested and fixes the issue on i.MX28, but hasn't been tested on i.MX23. Signed-off-by: Kristina Martšenko Reviewed-by: Marek Vasut Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/adc/mxs-lradc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/iio/adc/mxs-lradc.c b/drivers/staging/iio/adc/mxs-lradc.c index 9a9b1a73aa8690..822cced84659a7 100644 --- a/drivers/staging/iio/adc/mxs-lradc.c +++ b/drivers/staging/iio/adc/mxs-lradc.c @@ -824,7 +824,7 @@ static int mxs_lradc_read_single(struct iio_dev *iio_dev, int chan, int *val) if (lradc->soc == IMX28_LRADC) mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0), LRADC_CTRL1); - mxs_lradc_reg_clear(lradc, 0xff, LRADC_CTRL0); + mxs_lradc_reg_clear(lradc, 0x1, LRADC_CTRL0); /* Enable / disable the divider per requirement */ if (test_bit(chan, &lradc->is_divided)) @@ -1272,7 +1272,7 @@ static int mxs_lradc_buffer_preenable(struct iio_dev *iio) mxs_lradc_reg_clear(lradc, lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET, LRADC_CTRL1); - mxs_lradc_reg_clear(lradc, 0xff, LRADC_CTRL0); + mxs_lradc_reg_clear(lradc, lradc->buffer_vchans, LRADC_CTRL0); for_each_set_bit(chan, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) { ctrl4_set |= chan << LRADC_CTRL4_LRADCSELECT_OFFSET(ofs); @@ -1305,7 +1305,7 @@ static int mxs_lradc_buffer_postdisable(struct iio_dev *iio) mxs_lradc_reg_clear(lradc, LRADC_DELAY_TRIGGER_LRADCS_MASK | LRADC_DELAY_KICK, LRADC_DELAY(0)); - mxs_lradc_reg_clear(lradc, 0xff, LRADC_CTRL0); + mxs_lradc_reg_clear(lradc, lradc->buffer_vchans, LRADC_CTRL0); if (lradc->soc == IMX28_LRADC) mxs_lradc_reg_clear(lradc, lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET, From 5fc76bbea3153b71689ebd61edab3b8868b04c27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristina=20Mart=C5=A1enko?= Date: Sun, 25 Jan 2015 18:28:22 +0200 Subject: [PATCH 246/788] iio: mxs-lradc: only update the buffer when its conversions have finished MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 89bb35e200bee745c539a96666e0792301ca40f1 upstream. Using the touchscreen while running buffered capture results in the buffer reporting lots of wrong values, often just zeros. This is because we push readings to the buffer every time a touchscreen interrupt arrives, including when the buffer's own conversions have not yet finished. So let's only push to the buffer when its conversions are ready. Signed-off-by: Kristina Martšenko Reviewed-by: Marek Vasut Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/adc/mxs-lradc.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/staging/iio/adc/mxs-lradc.c b/drivers/staging/iio/adc/mxs-lradc.c index 822cced84659a7..351339ccaad671 100644 --- a/drivers/staging/iio/adc/mxs-lradc.c +++ b/drivers/staging/iio/adc/mxs-lradc.c @@ -1152,10 +1152,12 @@ static irqreturn_t mxs_lradc_handle_irq(int irq, void *data) LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2)); } - if (iio_buffer_enabled(iio)) - iio_trigger_poll(iio->trig); - else if (reg & LRADC_CTRL1_LRADC_IRQ(0)) + if (iio_buffer_enabled(iio)) { + if (reg & lradc->buffer_vchans) + iio_trigger_poll(iio->trig); + } else if (reg & LRADC_CTRL1_LRADC_IRQ(0)) { complete(&lradc->completion); + } mxs_lradc_reg_clear(lradc, reg & clr_irq, LRADC_CTRL1); From d3616899d426f40854d104eae0cd7dd6d13268f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Urs=20F=C3=A4ssler?= Date: Mon, 2 Feb 2015 17:12:23 +0100 Subject: [PATCH 247/788] iio: ad5686: fix optional reference voltage declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit da019f59cb16570e78feaf10380ac65a3a06861e upstream. When not using the "_optional" function, a dummy regulator is returned and the driver fails to initialize. Signed-off-by: Urs Fässler Acked-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/dac/ad5686.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index f57562aa396f44..15c73e20272d87 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -322,7 +322,7 @@ static int ad5686_probe(struct spi_device *spi) st = iio_priv(indio_dev); spi_set_drvdata(spi, indio_dev); - st->reg = devm_regulator_get(&spi->dev, "vcc"); + st->reg = devm_regulator_get_optional(&spi->dev, "vcc"); if (!IS_ERR(st->reg)) { ret = regulator_enable(st->reg); if (ret) From f3b2ffcff53dbd5358c04c1d8582dcf5b3421643 Mon Sep 17 00:00:00 2001 From: Angelo Compagnucci Date: Wed, 4 Feb 2015 15:14:26 +0100 Subject: [PATCH 248/788] iio:adc:mcp3422 Fix incorrect scales table commit 9e128ced3851d2802b6db870f6b2e93f449ce013 upstream. This patch fixes uncorrect order of mcp3422_scales table, the values was erroneously transposed. It removes also an unused array and a wrong comment. Signed-off-by: Angelo Compagnucci Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/mcp3422.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/drivers/iio/adc/mcp3422.c b/drivers/iio/adc/mcp3422.c index 51672256072bc8..b96c636470ef50 100644 --- a/drivers/iio/adc/mcp3422.c +++ b/drivers/iio/adc/mcp3422.c @@ -58,20 +58,11 @@ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ } -/* LSB is in nV to eliminate floating point */ -static const u32 rates_to_lsb[] = {1000000, 250000, 62500, 15625}; - -/* - * scales calculated as: - * rates_to_lsb[sample_rate] / (1 << pga); - * pga is 1 for 0, 2 - */ - static const int mcp3422_scales[4][4] = { - { 1000000, 250000, 62500, 15625 }, - { 500000 , 125000, 31250, 7812 }, - { 250000 , 62500 , 15625, 3906 }, - { 125000 , 31250 , 7812 , 1953 } }; + { 1000000, 500000, 250000, 125000 }, + { 250000 , 125000, 62500 , 31250 }, + { 62500 , 31250 , 15625 , 7812 }, + { 15625 , 7812 , 3906 , 1953 } }; /* Constant msleep times for data acquisitions */ static const int mcp3422_read_times[4] = { From dc77d1f2ff85e75ff2c9ac3ec462116468f98a63 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Thu, 12 Feb 2015 23:58:41 -0800 Subject: [PATCH 249/788] IIO: si7020: Allocate correct amount of memory in devm_iio_device_alloc commit e01becbad300712a28f29b666e685536f45e83bc upstream. Since only a pointer to struct i2c_client is stored in a private area of IIO device created by the driver there's no need to allocate sizeof(struct i2c_client) worth of storage. Pushed to stable as this is linked to the revert patch previously. Without this followup the original patch looks sensible. Signed-off-by: Andrey Smirnov Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/humidity/si7020.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/humidity/si7020.c b/drivers/iio/humidity/si7020.c index b54164677b898d..8e7f9f314fec96 100644 --- a/drivers/iio/humidity/si7020.c +++ b/drivers/iio/humidity/si7020.c @@ -126,7 +126,7 @@ static int si7020_probe(struct i2c_client *client, /* Wait the maximum power-up time after software reset. */ msleep(15); - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*client)); + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; From 75552ee54adb3f21719afd4e335b23a8a39b68b5 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sat, 14 Feb 2015 11:32:17 +0000 Subject: [PATCH 250/788] Revert "iio:humidity:si7020: fix pointer to i2c client" commit e765537add38cf7967efa11999bb5daf84a6517d upstream. This reverts commit e0922e5e3ccb78aa0152e93dfbd1755ac39c8582. Requested by Andrey Smirnov. It incorrectly assumes that the level of indirection is not needed which is not true(probably because the driver incorrectly allocates sizeof(*client) instead of sizeof(*data) via devm_iio_device_alloc). If you look at the code of the probe function(see below) it is easy to see that what is being stored in the private memory of the IIO device instance is not a copy of a 'struct i2c_client' but a pointer to an instance passed as an argument to the probe function. struct i2c_client **data; int ret; < Some code skipped > indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*client)); if (!indio_dev) return -ENOMEM; data = iio_priv(indio_dev); *data = client; Without reverting this change any read of a raw value of this sensor leads to a kernel oops due to a NULL pointer de-reference on my hardware setup. Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/humidity/si7020.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/humidity/si7020.c b/drivers/iio/humidity/si7020.c index 8e7f9f314fec96..fa3b809aff5efd 100644 --- a/drivers/iio/humidity/si7020.c +++ b/drivers/iio/humidity/si7020.c @@ -45,12 +45,12 @@ static int si7020_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { - struct i2c_client *client = iio_priv(indio_dev); + struct i2c_client **client = iio_priv(indio_dev); int ret; switch (mask) { case IIO_CHAN_INFO_RAW: - ret = i2c_smbus_read_word_data(client, + ret = i2c_smbus_read_word_data(*client, chan->type == IIO_TEMP ? SI7020CMD_TEMP_HOLD : SI7020CMD_RH_HOLD); From d2c34feb23e605beeca80ce1780ac5b6614838e7 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Tue, 10 Feb 2015 10:36:36 +0200 Subject: [PATCH 251/788] mei: make device disabled on stop unconditionally commit 6c15a8516b8118eb19a59fd0bd22df41b9101c32 upstream. Set the internal device state to to disabled after hardware reset in stop flow. This will cover cases when driver was not brought to disabled state because of an error and in stop flow we wish not to retry the reset. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/init.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 9306219d567544..6ad049a08e4d9d 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -341,6 +341,8 @@ void mei_stop(struct mei_device *dev) dev->dev_state = MEI_DEV_POWER_DOWN; mei_reset(dev); + /* move device to disabled state unconditionally */ + dev->dev_state = MEI_DEV_DISABLED; mutex_unlock(&dev->device_lock); From f3ee1f705fd616cd6471db75ab7873fe240fa813 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Mon, 9 Feb 2015 17:17:43 +0000 Subject: [PATCH 252/788] Btrfs: fix fsync race leading to ordered extent memory leaks commit 4d884fceaa2c838abb598778813e93f6d9fea723 upstream. We can have multiple fsync operations against the same file during the same transaction and they can collect the same ordered extents while they don't complete (still accessible from the inode's ordered tree). If this happens, those ordered extents will never get their reference counts decremented to 0, leading to memory leaks and inode leaks (an iput for an ordered extent's inode is scheduled only when the ordered extent's refcount drops to 0). The following sequence diagram explains this race: CPU 1 CPU 2 btrfs_sync_file() btrfs_sync_file() mutex_lock(inode->i_mutex) btrfs_log_inode() btrfs_get_logged_extents() --> collects ordered extent X --> increments ordered extent X's refcount btrfs_submit_logged_extents() mutex_unlock(inode->i_mutex) mutex_lock(inode->i_mutex) btrfs_sync_log() btrfs_wait_logged_extents() --> list_del_init(&ordered->log_list) btrfs_log_inode() btrfs_get_logged_extents() --> Adds ordered extent X to logged_list because at this point: list_empty(&ordered->log_list) && test_bit(BTRFS_ORDERED_LOGGED, &ordered->flags) == 0 --> Increments ordered extent X's refcount --> check if ordered extent's io is finished or not, start it if necessary and wait for it to finish --> sets bit BTRFS_ORDERED_LOGGED on ordered extent X's flags and adds it to trans->ordered btrfs_sync_log() finishes btrfs_submit_logged_extents() btrfs_log_inode() finishes mutex_unlock(inode->i_mutex) btrfs_sync_file() finishes btrfs_sync_log() btrfs_wait_logged_extents() --> Sees ordered extent X has the bit BTRFS_ORDERED_LOGGED set in its flags --> X's refcount is untouched btrfs_sync_log() finishes btrfs_sync_file() finishes btrfs_commit_transaction() --> called by transaction kthread for e.g. btrfs_wait_pending_ordered() --> waits for ordered extent X to complete --> decrements ordered extent X's refcount by 1 only, corresponding to the increment done by the fsync task ran by CPU 1 In the scenario of the above diagram, after the transaction commit, the ordered extent will remain with a refcount of 1 forever, leaking the ordered extent structure and preventing the i_count of its inode from ever decreasing to 0, since the delayed iput is scheduled only when the ordered extent's refcount drops to 0, preventing the inode from ever being evicted by the VFS. Fix this by using the flag BTRFS_ORDERED_LOGGED differently. Use it to mean that an ordered extent is already being processed by an fsync call, which will attach it to the current transaction, preventing it from being collected by subsequent fsync operations against the same inode. This race was introduced with the following change (added in 3.19 and backported to stable 3.18 and 3.17): Btrfs: make sure logged extents complete in the current transaction V3 commit 50d9aa99bd35c77200e0e3dd7a72274f8304701f I ran into this issue while running xfstests/generic/113 in a loop, which failed about 1 out of 10 runs with the following warning in dmesg: [ 2612.440038] WARNING: CPU: 4 PID: 22057 at fs/btrfs/disk-io.c:3558 free_fs_root+0x36/0x133 [btrfs]() [ 2612.442810] Modules linked in: btrfs crc32c_generic xor raid6_pq nfsd auth_rpcgss oid_registry nfs_acl nfs lockd grace fscache sunrpc loop processor parport_pc parport psmouse therma l_sys i2c_piix4 serio_raw pcspkr evdev microcode button i2c_core ext4 crc16 jbd2 mbcache sd_mod sg sr_mod cdrom virtio_scsi ata_generic virtio_pci ata_piix virtio_ring libata virtio flo ppy e1000 scsi_mod [last unloaded: btrfs] [ 2612.452711] CPU: 4 PID: 22057 Comm: umount Tainted: G W 3.19.0-rc5-btrfs-next-4+ #1 [ 2612.454921] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.7.5-0-ge51488c-20140602_164612-nilsson.home.kraxel.org 04/01/2014 [ 2612.457709] 0000000000000009 ffff8801342c3c78 ffffffff8142425e ffff88023ec8f2d8 [ 2612.459829] 0000000000000000 ffff8801342c3cb8 ffffffff81045308 ffff880046460000 [ 2612.461564] ffffffffa036da56 ffff88003d07b000 ffff880046460000 ffff880046460068 [ 2612.463163] Call Trace: [ 2612.463719] [] dump_stack+0x4c/0x65 [ 2612.464789] [] warn_slowpath_common+0xa1/0xbb [ 2612.466026] [] ? free_fs_root+0x36/0x133 [btrfs] [ 2612.467247] [] warn_slowpath_null+0x1a/0x1c [ 2612.468416] [] free_fs_root+0x36/0x133 [btrfs] [ 2612.469625] [] btrfs_drop_and_free_fs_root+0x93/0x9b [btrfs] [ 2612.471251] [] btrfs_free_fs_roots+0xa4/0xd6 [btrfs] [ 2612.472536] [] ? wait_for_completion+0x24/0x26 [ 2612.473742] [] close_ctree+0x1f3/0x33c [btrfs] [ 2612.475477] [] ? destroy_workqueue+0x148/0x1ba [ 2612.476695] [] btrfs_put_super+0x19/0x1b [btrfs] [ 2612.477911] [] generic_shutdown_super+0x73/0xef [ 2612.479106] [] kill_anon_super+0x13/0x1e [ 2612.480226] [] btrfs_kill_super+0x17/0x23 [btrfs] [ 2612.481471] [] deactivate_locked_super+0x3b/0x50 [ 2612.482686] [] deactivate_super+0x3f/0x43 [ 2612.483791] [] cleanup_mnt+0x59/0x78 [ 2612.484842] [] __cleanup_mnt+0x12/0x14 [ 2612.485900] [] task_work_run+0x8f/0xbc [ 2612.486960] [] do_notify_resume+0x5a/0x6b [ 2612.488083] [] ? trace_hardirqs_on_thunk+0x3a/0x3f [ 2612.489333] [] int_signal+0x12/0x17 [ 2612.490353] ---[ end trace 54a960a6bdcb8d93 ]--- [ 2612.557253] VFS: Busy inodes after unmount of sdb. Self-destruct in 5 seconds. Have a nice day... Kmemleak confirmed the ordered extent leak (and btrfs inode specific structures such as delayed nodes): $ cat /sys/kernel/debug/kmemleak unreferenced object 0xffff880154290db0 (size 576): comm "btrfsck", pid 21980, jiffies 4295542503 (age 1273.412s) hex dump (first 32 bytes): 01 40 00 00 01 00 00 00 b0 1d f1 4e 01 88 ff ff .@.........N.... 00 00 00 00 00 00 00 00 c8 0d 29 54 01 88 ff ff ..........)T.... backtrace: [] kmemleak_update_trace+0x4c/0x6a [] radix_tree_node_alloc+0x6d/0x83 [] __radix_tree_create+0x109/0x190 [] radix_tree_insert+0x30/0xac [] btrfs_get_or_create_delayed_node+0x130/0x187 [btrfs] [] btrfs_delayed_delete_inode_ref+0x32/0xac [btrfs] [] __btrfs_unlink_inode+0xee/0x288 [btrfs] [] btrfs_unlink_inode+0x1e/0x40 [btrfs] [] btrfs_unlink+0x60/0x9b [btrfs] [] vfs_unlink+0x9c/0xed [] do_unlinkat+0x12c/0x1fa [] SyS_unlinkat+0x29/0x2b [] system_call_fastpath+0x12/0x17 [] 0xffffffffffffffff unreferenced object 0xffff88014ef11db0 (size 576): comm "rm", pid 22009, jiffies 4295542593 (age 1273.052s) hex dump (first 32 bytes): 02 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 c8 1d f1 4e 01 88 ff ff ...........N.... backtrace: [] kmemleak_update_trace+0x4c/0x6a [] radix_tree_node_alloc+0x6d/0x83 [] __radix_tree_create+0x109/0x190 [] radix_tree_insert+0x30/0xac [] btrfs_get_or_create_delayed_node+0x130/0x187 [btrfs] [] btrfs_delayed_delete_inode_ref+0x32/0xac [btrfs] [] __btrfs_unlink_inode+0xee/0x288 [btrfs] [] btrfs_unlink_inode+0x1e/0x40 [btrfs] [] btrfs_unlink+0x60/0x9b [btrfs] [] vfs_unlink+0x9c/0xed [] do_unlinkat+0x12c/0x1fa [] SyS_unlinkat+0x29/0x2b [] system_call_fastpath+0x12/0x17 [] 0xffffffffffffffff unreferenced object 0xffff8800336feda8 (size 584): comm "aio-stress", pid 22031, jiffies 4295543006 (age 1271.400s) hex dump (first 32 bytes): 00 40 3e 00 00 00 00 00 00 00 8f 42 00 00 00 00 .@>........B.... 00 00 01 00 00 00 00 00 00 00 01 00 00 00 00 00 ................ backtrace: [] create_object+0x172/0x29a [] kmemleak_alloc+0x25/0x41 [] kmemleak_alloc_recursive.constprop.52+0x16/0x18 [] kmem_cache_alloc+0xf7/0x198 [] __btrfs_add_ordered_extent+0x43/0x309 [btrfs] [] btrfs_add_ordered_extent_dio+0x12/0x14 [btrfs] [] btrfs_get_blocks_direct+0x3ef/0x571 [btrfs] [] do_blockdev_direct_IO+0x62a/0xb47 [] __blockdev_direct_IO+0x34/0x36 [] btrfs_direct_IO+0x16a/0x1e8 [btrfs] [] generic_file_direct_write+0xb8/0x12d [] btrfs_file_write_iter+0x24b/0x42f [btrfs] [] aio_run_iocb+0x2b7/0x32e [] do_io_submit+0x26e/0x2ff [] SyS_io_submit+0x10/0x12 [] system_call_fastpath+0x12/0x17 Signed-off-by: Filipe Manana Signed-off-by: Chris Mason Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/ordered-data.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index 534544e08f7694..157cc54fc63486 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -452,9 +452,7 @@ void btrfs_get_logged_extents(struct inode *inode, continue; if (entry_end(ordered) <= start) break; - if (!list_empty(&ordered->log_list)) - continue; - if (test_bit(BTRFS_ORDERED_LOGGED, &ordered->flags)) + if (test_and_set_bit(BTRFS_ORDERED_LOGGED, &ordered->flags)) continue; list_add(&ordered->log_list, logged_list); atomic_inc(&ordered->refs); @@ -511,8 +509,7 @@ void btrfs_wait_logged_extents(struct btrfs_trans_handle *trans, wait_event(ordered->wait, test_bit(BTRFS_ORDERED_IO_DONE, &ordered->flags)); - if (!test_and_set_bit(BTRFS_ORDERED_LOGGED, &ordered->flags)) - list_add_tail(&ordered->trans_list, &trans->ordered); + list_add_tail(&ordered->trans_list, &trans->ordered); spin_lock_irq(&log->log_extents_lock[index]); } spin_unlock_irq(&log->log_extents_lock[index]); From a0d0ff380cb70231be173dbbcce430da873d92a7 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Tue, 24 Feb 2015 18:57:18 +0100 Subject: [PATCH 253/788] btrfs: fix lost return value due to variable shadowing commit 1932b7be973b554ffe20a5bba6ffaed6fa995cdc upstream. A block-local variable stores error code but btrfs_get_blocks_direct may not return it in the end as there's a ret defined in the function scope. Fixes: d187663ef24c ("Btrfs: lock extents as we map them in DIO") Signed-off-by: David Sterba Signed-off-by: Chris Mason Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/inode.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 8bf326affb9440..35911f057a7750 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -7208,7 +7208,6 @@ static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock, ((BTRFS_I(inode)->flags & BTRFS_INODE_NODATACOW) && em->block_start != EXTENT_MAP_HOLE)) { int type; - int ret; u64 block_start, orig_start, orig_block_len, ram_bytes; if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags)) From f4a7f91a08bd06f430c72b482c46386b4f3268e7 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Sun, 1 Mar 2015 20:36:00 +0000 Subject: [PATCH 254/788] Btrfs: fix data loss in the fast fsync path commit 3a8b36f378060d20062a0918e99fae39ff077bf0 upstream. When using the fast file fsync code path we can miss the fact that new writes happened since the last file fsync and therefore return without waiting for the IO to finish and write the new extents to the fsync log. Here's an example scenario where the fsync will miss the fact that new file data exists that wasn't yet durably persisted: 1. fs_info->last_trans_committed == N - 1 and current transaction is transaction N (fs_info->generation == N); 2. do a buffered write; 3. fsync our inode, this clears our inode's full sync flag, starts an ordered extent and waits for it to complete - when it completes at btrfs_finish_ordered_io(), the inode's last_trans is set to the value N (via btrfs_update_inode_fallback -> btrfs_update_inode -> btrfs_set_inode_last_trans); 4. transaction N is committed, so fs_info->last_trans_committed is now set to the value N and fs_info->generation remains with the value N; 5. do another buffered write, when this happens btrfs_file_write_iter sets our inode's last_trans to the value N + 1 (that is fs_info->generation + 1 == N + 1); 6. transaction N + 1 is started and fs_info->generation now has the value N + 1; 7. transaction N + 1 is committed, so fs_info->last_trans_committed is set to the value N + 1; 8. fsync our inode - because it doesn't have the full sync flag set, we only start the ordered extent, we don't wait for it to complete (only in a later phase) therefore its last_trans field has the value N + 1 set previously by btrfs_file_write_iter(), and so we have: inode->last_trans <= fs_info->last_trans_committed (N + 1) (N + 1) Which made us not log the last buffered write and exit the fsync handler immediately, returning success (0) to user space and resulting in data loss after a crash. This can actually be triggered deterministically and the following excerpt from a testcase I made for xfstests triggers the issue. It moves a dummy file across directories and then fsyncs the old parent directory - this is just to trigger a transaction commit, so moving files around isn't directly related to the issue but it was chosen because running 'sync' for example does more than just committing the current transaction, as it flushes/waits for all file data to be persisted. The issue can also happen at random periods, since the transaction kthread periodicaly commits the current transaction (about every 30 seconds by default). The body of the test is: _scratch_mkfs >> $seqres.full 2>&1 _init_flakey _mount_flakey # Create our main test file 'foo', the one we check for data loss. # By doing an fsync against our file, it makes btrfs clear the 'needs_full_sync' # bit from its flags (btrfs inode specific flags). $XFS_IO_PROG -f -c "pwrite -S 0xaa 0 8K" \ -c "fsync" $SCRATCH_MNT/foo | _filter_xfs_io # Now create one other file and 2 directories. We will move this second file # from one directory to the other later because it forces btrfs to commit its # currently open transaction if we fsync the old parent directory. This is # necessary to trigger the data loss bug that affected btrfs. mkdir $SCRATCH_MNT/testdir_1 touch $SCRATCH_MNT/testdir_1/bar mkdir $SCRATCH_MNT/testdir_2 # Make sure everything is durably persisted. sync # Write more 8Kb of data to our file. $XFS_IO_PROG -c "pwrite -S 0xbb 8K 8K" $SCRATCH_MNT/foo | _filter_xfs_io # Move our 'bar' file into a new directory. mv $SCRATCH_MNT/testdir_1/bar $SCRATCH_MNT/testdir_2/bar # Fsync our first directory. Because it had a file moved into some other # directory, this made btrfs commit the currently open transaction. This is # a condition necessary to trigger the data loss bug. $XFS_IO_PROG -c "fsync" $SCRATCH_MNT/testdir_1 # Now fsync our main test file. If the fsync succeeds, we expect the 8Kb of # data we wrote previously to be persisted and available if a crash happens. # This did not happen with btrfs, because of the transaction commit that # happened when we fsynced the parent directory. $XFS_IO_PROG -c "fsync" $SCRATCH_MNT/foo # Simulate a crash/power loss. _load_flakey_table $FLAKEY_DROP_WRITES _unmount_flakey _load_flakey_table $FLAKEY_ALLOW_WRITES _mount_flakey # Now check that all data we wrote before are available. echo "File content after log replay:" od -t x1 $SCRATCH_MNT/foo status=0 exit The expected golden output for the test, which is what we get with this fix applied (or when running against ext3/4 and xfs), is: wrote 8192/8192 bytes at offset 0 XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 8192/8192 bytes at offset 8192 XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) File content after log replay: 0000000 aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa * 0020000 bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb * 0040000 Without this fix applied, the output shows the test file does not have the second 8Kb extent that we successfully fsynced: wrote 8192/8192 bytes at offset 0 XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 8192/8192 bytes at offset 8192 XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) File content after log replay: 0000000 aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa * 0020000 So fix this by skipping the fsync only if we're doing a full sync and if the inode's last_trans is <= fs_info->last_trans_committed, or if the inode is already in the log. Also remove setting the inode's last_trans in btrfs_file_write_iter since it's useless/unreliable. Also because btrfs_file_write_iter no longer sets inode->last_trans to fs_info->generation + 1, don't set last_trans to 0 if we bail out and don't bail out if last_trans is 0, otherwise something as simple as the following example wouldn't log the second write on the last fsync: 1. write to file 2. fsync file 3. fsync file |--> btrfs_inode_in_log() returns true and it set last_trans to 0 4. write to file |--> btrfs_file_write_iter() no longers sets last_trans, so it remained with a value of 0 5. fsync |--> inode->last_trans == 0, so it bails out without logging the second write A test case for xfstests will be sent soon. Signed-off-by: Filipe Manana Signed-off-by: Chris Mason Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/file.c | 56 ++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index e4090259569bcc..5529ca84496f18 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1811,22 +1811,10 @@ static ssize_t btrfs_file_write_iter(struct kiocb *iocb, mutex_unlock(&inode->i_mutex); /* - * we want to make sure fsync finds this change - * but we haven't joined a transaction running right now. - * - * Later on, someone is sure to update the inode and get the - * real transid recorded. - * - * We set last_trans now to the fs_info generation + 1, - * this will either be one more than the running transaction - * or the generation used for the next transaction if there isn't - * one running right now. - * * We also have to set last_sub_trans to the current log transid, * otherwise subsequent syncs to a file that's been synced in this * transaction will appear to have already occured. */ - BTRFS_I(inode)->last_trans = root->fs_info->generation + 1; BTRFS_I(inode)->last_sub_trans = root->log_transid; if (num_written > 0) { err = generic_write_sync(file, pos, num_written); @@ -1959,25 +1947,37 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) atomic_inc(&root->log_batch); /* - * check the transaction that last modified this inode - * and see if its already been committed - */ - if (!BTRFS_I(inode)->last_trans) { - mutex_unlock(&inode->i_mutex); - goto out; - } - - /* - * if the last transaction that changed this file was before - * the current transaction, we can bail out now without any - * syncing + * If the last transaction that changed this file was before the current + * transaction and we have the full sync flag set in our inode, we can + * bail out now without any syncing. + * + * Note that we can't bail out if the full sync flag isn't set. This is + * because when the full sync flag is set we start all ordered extents + * and wait for them to fully complete - when they complete they update + * the inode's last_trans field through: + * + * btrfs_finish_ordered_io() -> + * btrfs_update_inode_fallback() -> + * btrfs_update_inode() -> + * btrfs_set_inode_last_trans() + * + * So we are sure that last_trans is up to date and can do this check to + * bail out safely. For the fast path, when the full sync flag is not + * set in our inode, we can not do it because we start only our ordered + * extents and don't wait for them to complete (that is when + * btrfs_finish_ordered_io runs), so here at this point their last_trans + * value might be less than or equals to fs_info->last_trans_committed, + * and setting a speculative last_trans for an inode when a buffered + * write is made (such as fs_info->generation + 1 for example) would not + * be reliable since after setting the value and before fsync is called + * any number of transactions can start and commit (transaction kthread + * commits the current transaction periodically), and a transaction + * commit does not start nor waits for ordered extents to complete. */ smp_mb(); if (btrfs_inode_in_log(inode, root->fs_info->generation) || - BTRFS_I(inode)->last_trans <= - root->fs_info->last_trans_committed) { - BTRFS_I(inode)->last_trans = 0; - + (full_sync && BTRFS_I(inode)->last_trans <= + root->fs_info->last_trans_committed)) { /* * We'v had everything committed since the last time we were * modified so clear this flag in case it was set for whatever From b0539dd5ecdcb164afc72aafd102db8626e42e49 Mon Sep 17 00:00:00 2001 From: Quentin Casasnovas Date: Tue, 3 Mar 2015 16:31:38 +0100 Subject: [PATCH 255/788] Btrfs:__add_inode_ref: out of bounds memory read when looking for extended ref. commit dd9ef135e3542ffc621c4eb7f0091870ec7a1504 upstream. Improper arithmetics when calculting the address of the extended ref could lead to an out of bounds memory read and kernel panic. Signed-off-by: Quentin Casasnovas Reviewed-by: David Sterba Signed-off-by: Chris Mason Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/tree-log.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index f78e9dc5d5747d..069ab24badaaf9 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -1010,7 +1010,7 @@ static inline int __add_inode_ref(struct btrfs_trans_handle *trans, base = btrfs_item_ptr_offset(leaf, path->slots[0]); while (cur_offset < item_size) { - extref = (struct btrfs_inode_extref *)base + cur_offset; + extref = (struct btrfs_inode_extref *)(base + cur_offset); victim_name_len = btrfs_inode_extref_name_len(leaf, extref); From d1feb25231a0653a395ed17baefc29aa42b43c9e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 12 Feb 2015 17:04:47 +0100 Subject: [PATCH 256/788] KVM: emulate: fix CMPXCHG8B on 32-bit hosts commit 4ff6f8e61eb7f96d3ca535c6d240f863ccd6fb7d upstream. This has been broken for a long time: it broke first in 2.6.35, then was almost fixed in 2.6.36 but this one-liner slipped through the cracks. The bug shows up as an infinite loop in Windows 7 (and newer) boot on 32-bit hosts without EPT. Windows uses CMPXCHG8B to write to page tables, which causes a page fault if running without EPT; the emulator is then called from kvm_mmu_page_fault. The loop then happens if the higher 4 bytes are not 0; the common case for this is that the NX bit (bit 63) is 1. Fixes: 6550e1f165f384f3a46b60a1be9aba4bc3c2adad Fixes: 16518d5ada690643453eb0aef3cc7841d3623c2d Reported-by: Erik Rull Tested-by: Erik Rull Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/emulate.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index de12c1d379f168..b24c2d84dc20d7 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -4863,7 +4863,8 @@ int x86_emulate_insn(struct x86_emulate_ctxt *ctxt) if (rc != X86EMUL_CONTINUE) goto done; } - ctxt->dst.orig_val = ctxt->dst.val; + /* Copy full 64-bit value for CMPXCHG8B. */ + ctxt->dst.orig_val64 = ctxt->dst.val64; special_insn: From 89fce062e1c6c6e2a3a94c68b93ddb65ba1744ad Mon Sep 17 00:00:00 2001 From: James Hogan Date: Tue, 24 Feb 2015 11:46:20 +0000 Subject: [PATCH 257/788] KVM: MIPS: Fix trace event to save PC directly commit b3cffac04eca9af46e1e23560a8ee22b1bd36d43 upstream. Currently the guest exit trace event saves the VCPU pointer to the structure, and the guest PC is retrieved by dereferencing it when the event is printed rather than directly from the trace record. This isn't safe as the printing may occur long afterwards, after the PC has changed and potentially after the VCPU has been freed. Usually this results in the same (wrong) PC being printed for multiple trace events. It also isn't portable as userland has no way to access the VCPU data structure when interpreting the trace record itself. Lets save the actual PC in the structure so that the correct value is accessible later. Fixes: 669e846e6c4e ("KVM/MIPS32: MIPS arch specific APIs for KVM") Signed-off-by: James Hogan Cc: Paolo Bonzini Cc: Ralf Baechle Cc: Marcelo Tosatti Cc: Gleb Natapov Cc: Steven Rostedt Cc: Ingo Molnar Cc: linux-mips@linux-mips.org Cc: kvm@vger.kernel.org Acked-by: Steven Rostedt Signed-off-by: Marcelo Tosatti Signed-off-by: Greg Kroah-Hartman --- arch/mips/kvm/trace.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/mips/kvm/trace.h b/arch/mips/kvm/trace.h index c1388d40663b01..bd6437f67dc03b 100644 --- a/arch/mips/kvm/trace.h +++ b/arch/mips/kvm/trace.h @@ -24,18 +24,18 @@ TRACE_EVENT(kvm_exit, TP_PROTO(struct kvm_vcpu *vcpu, unsigned int reason), TP_ARGS(vcpu, reason), TP_STRUCT__entry( - __field(struct kvm_vcpu *, vcpu) + __field(unsigned long, pc) __field(unsigned int, reason) ), TP_fast_assign( - __entry->vcpu = vcpu; + __entry->pc = vcpu->arch.pc; __entry->reason = reason; ), TP_printk("[%s]PC: 0x%08lx", kvm_mips_exit_types_str[__entry->reason], - __entry->vcpu->arch.pc) + __entry->pc) ); #endif /* _TRACE_KVM_H */ From a5c2f30b5b29313198cf430e7b48949eb97ea717 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 23 Feb 2015 13:41:14 +0100 Subject: [PATCH 258/788] uas: Add US_FL_NO_REPORT_OPCODES for JMicron JMS539 commit 59e980efafd27df83a5c85c054f906d82bcbf752 upstream. Like the JMicron JMS567 enclosures with the JMS539 choke on report-opcodes, so avoid it. Tested-and-reported-by: Tom Arild Naess Signed-off-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_uas.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h index dbc00e56c7f5c1..82570425fdfe38 100644 --- a/drivers/usb/storage/unusual_uas.h +++ b/drivers/usb/storage/unusual_uas.h @@ -113,6 +113,13 @@ UNUSUAL_DEV(0x0bc2, 0xab2a, 0x0000, 0x9999, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NO_ATA_1X), +/* Reported-by: Tom Arild Naess */ +UNUSUAL_DEV(0x152d, 0x0539, 0x0000, 0x9999, + "JMicron", + "JMS539", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_REPORT_OPCODES), + /* Reported-by: Claudio Bizzarri */ UNUSUAL_DEV(0x152d, 0x0567, 0x0000, 0x9999, "JMicron", From 8356c502a889d7796e36989e5c59fa5e82e5ceb9 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sun, 15 Feb 2015 11:57:53 +0700 Subject: [PATCH 259/788] Revert "USB: serial: make bulk_out_size a lower limit" commit bc4b1f486fe69b86769e07c8edce472327a8462b upstream. This reverts commit 5083fd7bdfe6760577235a724cf6dccae13652c2. A bulk-out size smaller than the end-point size is indeed valid. The offending commit broke the usb-debug driver for EHCI debug devices, which use 8-byte buffers. Fixes: 5083fd7bdfe6 ("USB: serial: make bulk_out_size a lower limit") Reported-by: "Li, Elvin" Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/usb-serial.c | 5 +++-- include/linux/usb/serial.h | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 475723c006f955..19842370a07f93 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -940,8 +940,9 @@ static int usb_serial_probe(struct usb_interface *interface, port = serial->port[i]; if (kfifo_alloc(&port->write_fifo, PAGE_SIZE, GFP_KERNEL)) goto probe_error; - buffer_size = max_t(int, serial->type->bulk_out_size, - usb_endpoint_maxp(endpoint)); + buffer_size = serial->type->bulk_out_size; + if (!buffer_size) + buffer_size = usb_endpoint_maxp(endpoint); port->bulk_out_size = buffer_size; port->bulk_out_endpointAddress = endpoint->bEndpointAddress; diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 9bb547c7bce7c7..704a1ab8240ca1 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -190,8 +190,7 @@ static inline void usb_set_serial_data(struct usb_serial *serial, void *data) * @num_ports: the number of different ports this device will have. * @bulk_in_size: minimum number of bytes to allocate for bulk-in buffer * (0 = end-point size) - * @bulk_out_size: minimum number of bytes to allocate for bulk-out buffer - * (0 = end-point size) + * @bulk_out_size: bytes to allocate for bulk-out buffer (0 = end-point size) * @calc_num_ports: pointer to a function to determine how many ports this * device has dynamically. It will be called after the probe() * callback is called, but before attach() From 63834eef1577cef63aee30b17bff109ddb558a3d Mon Sep 17 00:00:00 2001 From: Michiel vd Garde Date: Fri, 27 Feb 2015 02:08:29 +0100 Subject: [PATCH 260/788] USB: serial: cp210x: Adding Seletek device id's commit 675af70856d7cc026be8b6ea7a8b9db10b8b38a1 upstream. These device ID's are not associated with the cp210x module currently, but should be. This patch allows the devices to operate upon connecting them to the usb bus as intended. Signed-off-by: Michiel van de Garde Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/cp210x.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index f40c856ff758d9..84ce2d74894c9c 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -147,6 +147,8 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x166A, 0x0305) }, /* Clipsal C-5000CT2 C-Bus Spectrum Colour Touchscreen */ { USB_DEVICE(0x166A, 0x0401) }, /* Clipsal L51xx C-Bus Architectural Dimmer */ { USB_DEVICE(0x166A, 0x0101) }, /* Clipsal 5560884 C-Bus Multi-room Audio Matrix Switcher */ + { USB_DEVICE(0x16C0, 0x09B0) }, /* Lunatico Seletek */ + { USB_DEVICE(0x16C0, 0x09B1) }, /* Lunatico Seletek */ { USB_DEVICE(0x16D6, 0x0001) }, /* Jablotron serial interface */ { USB_DEVICE(0x16DC, 0x0010) }, /* W-IE-NE-R Plein & Baus GmbH PL512 Power Supply */ { USB_DEVICE(0x16DC, 0x0011) }, /* W-IE-NE-R Plein & Baus GmbH RCM Remote Control for MARATON Power Supply */ From 4e1f2e34fdfc8df71c58ef2552cecb5df848979a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 18 Feb 2015 11:51:07 +0700 Subject: [PATCH 261/788] USB: mxuport: fix null deref when used as a console commit db81de767e375743ebb0ad2bcad3326962c2b67e upstream. Fix null-pointer dereference at probe when the device is used as a console, in which case the tty argument to open will be NULL. Fixes: ee467a1f2066 ("USB: serial: add Moxa UPORT 12XX/14XX/16XX driver") Signed-off-by: Johan Hovold Acked-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/mxuport.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/mxuport.c b/drivers/usb/serial/mxuport.c index ab1d690274ae52..460a4066996785 100644 --- a/drivers/usb/serial/mxuport.c +++ b/drivers/usb/serial/mxuport.c @@ -1284,7 +1284,8 @@ static int mxuport_open(struct tty_struct *tty, struct usb_serial_port *port) } /* Initial port termios */ - mxuport_set_termios(tty, port, NULL); + if (tty) + mxuport_set_termios(tty, port, NULL); /* * TODO: use RQ_VENDOR_GET_MSR, once we know what it From af774ee88fa197407f43205f0b427bb206998ef5 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 13 Feb 2015 10:54:53 -0500 Subject: [PATCH 262/788] USB: usbfs: don't leak kernel data in siginfo commit f0c2b68198589249afd2b1f2c4e8de8c03e19c16 upstream. When a signal is delivered, the information in the siginfo structure is copied to userspace. Good security practice dicatates that the unused fields in this structure should be initialized to 0 so that random kernel stack data isn't exposed to the user. This patch adds such an initialization to the two places where usbfs raises signals. Signed-off-by: Alan Stern Reported-by: Dave Mielke Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 0b59731c302133..e500243803d87b 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -501,6 +501,7 @@ static void async_completed(struct urb *urb) as->status = urb->status; signr = as->signr; if (signr) { + memset(&sinfo, 0, sizeof(sinfo)); sinfo.si_signo = as->signr; sinfo.si_errno = as->status; sinfo.si_code = SI_ASYNCIO; @@ -2371,6 +2372,7 @@ static void usbdev_remove(struct usb_device *udev) wake_up_all(&ps->wait); list_del_init(&ps->list); if (ps->discsignr) { + memset(&sinfo, 0, sizeof(sinfo)); sinfo.si_signo = ps->discsignr; sinfo.si_errno = EPIPE; sinfo.si_code = SI_ASYNCIO; From d8a475bd78c31403dbc7d1e342d5afdd5d1f79e2 Mon Sep 17 00:00:00 2001 From: Mark Glover Date: Fri, 13 Feb 2015 09:04:39 +0000 Subject: [PATCH 263/788] USB: ftdi_sio: add PIDs for Actisense USB devices commit f6950344d3cf4a1e231b5828b50c4ac168db3886 upstream. These product identifiers (PID) all deal with marine NMEA format data used on motor boats and yachts. We supply the programmed devices to Chetco, for use inside their equipment. The PIDs are a direct copy of our Windows device drivers (FTDI drivers with altered PIDs). Signed-off-by: Mark Glover [johan: edit commit message slightly ] Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 17 +++++++++++++++++ drivers/usb/serial/ftdi_sio_ids.h | 20 ++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 1ebb351b9e9a59..651dc1ba46c345 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -978,6 +978,23 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE_INTERFACE_NUMBER(INFINEON_VID, INFINEON_TRIBOARD_PID, 1) }, /* GE Healthcare devices */ { USB_DEVICE(GE_HEALTHCARE_VID, GE_HEALTHCARE_NEMO_TRACKER_PID) }, + /* Active Research (Actisense) devices */ + { USB_DEVICE(FTDI_VID, ACTISENSE_NDC_PID) }, + { USB_DEVICE(FTDI_VID, ACTISENSE_USG_PID) }, + { USB_DEVICE(FTDI_VID, ACTISENSE_NGT_PID) }, + { USB_DEVICE(FTDI_VID, ACTISENSE_NGW_PID) }, + { USB_DEVICE(FTDI_VID, ACTISENSE_D9AC_PID) }, + { USB_DEVICE(FTDI_VID, ACTISENSE_D9AD_PID) }, + { USB_DEVICE(FTDI_VID, ACTISENSE_D9AE_PID) }, + { USB_DEVICE(FTDI_VID, ACTISENSE_D9AF_PID) }, + { USB_DEVICE(FTDI_VID, CHETCO_SEAGAUGE_PID) }, + { USB_DEVICE(FTDI_VID, CHETCO_SEASWITCH_PID) }, + { USB_DEVICE(FTDI_VID, CHETCO_SEASMART_NMEA2000_PID) }, + { USB_DEVICE(FTDI_VID, CHETCO_SEASMART_ETHERNET_PID) }, + { USB_DEVICE(FTDI_VID, CHETCO_SEASMART_WIFI_PID) }, + { USB_DEVICE(FTDI_VID, CHETCO_SEASMART_DISPLAY_PID) }, + { USB_DEVICE(FTDI_VID, CHETCO_SEASMART_LITE_PID) }, + { USB_DEVICE(FTDI_VID, CHETCO_SEASMART_ANALOG_PID) }, { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index e52409c9be999f..4d3da89cd8dd3b 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -1438,3 +1438,23 @@ */ #define GE_HEALTHCARE_VID 0x1901 #define GE_HEALTHCARE_NEMO_TRACKER_PID 0x0015 + +/* + * Active Research (Actisense) devices + */ +#define ACTISENSE_NDC_PID 0xD9A8 /* NDC USB Serial Adapter */ +#define ACTISENSE_USG_PID 0xD9A9 /* USG USB Serial Adapter */ +#define ACTISENSE_NGT_PID 0xD9AA /* NGT NMEA2000 Interface */ +#define ACTISENSE_NGW_PID 0xD9AB /* NGW NMEA2000 Gateway */ +#define ACTISENSE_D9AC_PID 0xD9AC /* Actisense Reserved */ +#define ACTISENSE_D9AD_PID 0xD9AD /* Actisense Reserved */ +#define ACTISENSE_D9AE_PID 0xD9AE /* Actisense Reserved */ +#define ACTISENSE_D9AF_PID 0xD9AF /* Actisense Reserved */ +#define CHETCO_SEAGAUGE_PID 0xA548 /* SeaGauge USB Adapter */ +#define CHETCO_SEASWITCH_PID 0xA549 /* SeaSwitch USB Adapter */ +#define CHETCO_SEASMART_NMEA2000_PID 0xA54A /* SeaSmart NMEA2000 Gateway */ +#define CHETCO_SEASMART_ETHERNET_PID 0xA54B /* SeaSmart Ethernet Gateway */ +#define CHETCO_SEASMART_WIFI_PID 0xA5AC /* SeaSmart Wifi Gateway */ +#define CHETCO_SEASMART_DISPLAY_PID 0xA5AD /* SeaSmart NMEA2000 Display */ +#define CHETCO_SEASMART_LITE_PID 0xA5AE /* SeaSmart Lite USB Adapter */ +#define CHETCO_SEASMART_ANALOG_PID 0xA5AF /* SeaSmart Analog Adapter */ From d3ecce096759db41ab4130e674e2a9debc5c135d Mon Sep 17 00:00:00 2001 From: Max Mansfield Date: Mon, 2 Mar 2015 18:38:02 -0700 Subject: [PATCH 264/788] usb: ftdi_sio: Add jtag quirk support for Cyber Cortex AV boards commit c7d373c3f0da2b2b78c4b1ce5ae41485b3ef848c upstream. This patch integrates Cyber Cortex AV boards with the existing ftdi_jtag_quirk in order to use serial port 0 with JTAG which is required by the manufacturers' software. Steps: 2 [ftdi_sio_ids.h] 1. Defined the device PID [ftdi_sio.c] 2. Added a macro declaration to the ids array, in order to enable the jtag quirk for the device. Signed-off-by: Max Mansfield Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 2 ++ drivers/usb/serial/ftdi_sio_ids.h | 3 +++ 2 files changed, 5 insertions(+) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 651dc1ba46c345..3086dec0ef53bb 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -799,6 +799,8 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(FTDI_VID, FTDI_ELSTER_UNICOM_PID) }, { USB_DEVICE(FTDI_VID, FTDI_PROPOX_JTAGCABLEII_PID) }, { USB_DEVICE(FTDI_VID, FTDI_PROPOX_ISPCABLEIII_PID) }, + { USB_DEVICE(FTDI_VID, CYBER_CORTEX_AV_PID), + .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(OLIMEX_VID, OLIMEX_ARM_USB_OCD_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(OLIMEX_VID, OLIMEX_ARM_USB_OCD_H_PID), diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index 4d3da89cd8dd3b..56b1b55c475169 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -38,6 +38,9 @@ #define FTDI_LUMEL_PD12_PID 0x6002 +/* Cyber Cortex AV by Fabulous Silicon (http://fabuloussilicon.com) */ +#define CYBER_CORTEX_AV_PID 0x8698 + /* * Marvell OpenRD Base, Client * http://www.open-rd.org From 1ebbda9ff931844cae217e11f59fe918f9df0dd0 Mon Sep 17 00:00:00 2001 From: George Cherian Date: Fri, 13 Feb 2015 10:13:24 +0530 Subject: [PATCH 265/788] usb: dwc3: dwc3-omap: Fix disable IRQ commit 96e5d31244c5542f5b2ea81d76f14ba4b8a7d440 upstream. In the wrapper the IRQ disable should be done by writing 1's to the IRQ*_CLR register. Existing code is broken because it instead writes zeros to IRQ*_SET register. Fix this by adding functions dwc3_omap_write_irqmisc_clr() and dwc3_omap_write_irq0_clr() which do the right thing. Fixes: 72246da40f37 ("usb: Introduce DesignWare USB3 DRD Driver") Signed-off-by: George Cherian Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-omap.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 172d64e585b61b..52e0c4e5e48efa 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -205,6 +205,18 @@ static void dwc3_omap_write_irq0_set(struct dwc3_omap *omap, u32 value) omap->irq0_offset, value); } +static void dwc3_omap_write_irqmisc_clr(struct dwc3_omap *omap, u32 value) +{ + dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_CLR_MISC + + omap->irqmisc_offset, value); +} + +static void dwc3_omap_write_irq0_clr(struct dwc3_omap *omap, u32 value) +{ + dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_CLR_0 - + omap->irq0_offset, value); +} + static void dwc3_omap_set_mailbox(struct dwc3_omap *omap, enum omap_dwc3_vbus_id_status status) { @@ -345,9 +357,23 @@ static void dwc3_omap_enable_irqs(struct dwc3_omap *omap) static void dwc3_omap_disable_irqs(struct dwc3_omap *omap) { + u32 reg; + /* disable all IRQs */ - dwc3_omap_write_irqmisc_set(omap, 0x00); - dwc3_omap_write_irq0_set(omap, 0x00); + reg = USBOTGSS_IRQO_COREIRQ_ST; + dwc3_omap_write_irq0_clr(omap, reg); + + reg = (USBOTGSS_IRQMISC_OEVT | + USBOTGSS_IRQMISC_DRVVBUS_RISE | + USBOTGSS_IRQMISC_CHRGVBUS_RISE | + USBOTGSS_IRQMISC_DISCHRGVBUS_RISE | + USBOTGSS_IRQMISC_IDPULLUP_RISE | + USBOTGSS_IRQMISC_DRVVBUS_FALL | + USBOTGSS_IRQMISC_CHRGVBUS_FALL | + USBOTGSS_IRQMISC_DISCHRGVBUS_FALL | + USBOTGSS_IRQMISC_IDPULLUP_FALL); + + dwc3_omap_write_irqmisc_clr(omap, reg); } static u64 dwc3_omap_dma_mask = DMA_BIT_MASK(32); From f735fd3314b2dcee082db379afc3c5074cef5bca Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Fri, 13 Feb 2015 12:12:53 +0100 Subject: [PATCH 266/788] usb: gadget: configfs: don't NUL-terminate (sub)compatible ids commit a0456399fb07155637a2b597b91cc1c63bc25141 upstream. The "Extended Compat ID OS Feature Descriptor Specification" does not require the (sub)compatible ids to be NUL-terminated, because they are placed in a fixed-size buffer and only unused parts of it should contain NULs. If the buffer is fully utilized, there is no place for NULs. Consequently, the code which uses desc->ext_compat_id never expects the data contained to be NUL terminated. If the compatible id is stored after sub-compatible id, and the compatible id is full length (8 bytes), the (useless) NUL terminator overwrites the first byte of the sub-compatible id. If the sub-compatible id is full length (8 bytes), the (useless) NUL terminator ends up out of the buffer. The situation can happen in the RNDIS function, where the buffer is a part of struct f_rndis_opts. The next member of struct f_rndis_opts is a mutex, so its first byte gets overwritten. The said byte is a part of a mutex'es member which contains the information on whether the muext is locked or not. This can lead to a deadlock, because, in a configfs-composed gadget when a function is linked into a configuration with config_usb_cfg_link(), usb_get_function() is called, which then calls rndis_alloc(), which tries locking the same mutex and (wrongly) finds it already locked. This patch eliminates NUL terminating of the (sub)compatible id. Fixes: da4243145fb1: "usb: gadget: configfs: OS Extended Compatibility descriptors support" Reported-by: Dan Carpenter Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/configfs.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 75648145dc1b68..c42765b3a060bc 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -1161,7 +1161,6 @@ static ssize_t interf_grp_compatible_id_store(struct usb_os_desc *desc, if (desc->opts_mutex) mutex_lock(desc->opts_mutex); memcpy(desc->ext_compat_id, page, l); - desc->ext_compat_id[l] = '\0'; if (desc->opts_mutex) mutex_unlock(desc->opts_mutex); @@ -1192,7 +1191,6 @@ static ssize_t interf_grp_sub_compatible_id_store(struct usb_os_desc *desc, if (desc->opts_mutex) mutex_lock(desc->opts_mutex); memcpy(desc->ext_compat_id + 8, page, l); - desc->ext_compat_id[l + 8] = '\0'; if (desc->opts_mutex) mutex_unlock(desc->opts_mutex); From 6ae021266fd88e9e1a7394a255e0c28b17e88b7d Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 24 Feb 2015 18:27:00 +0200 Subject: [PATCH 267/788] usb: XHCI: platform: Move the Marvell quirks after the enabling the clocks commit 1e7e4fb66489cc84366656ca5318f1cb61afd4ba upstream. The commit 973747928514 ("usb: host: xhci-plat: add support for the Armada 375/38x XHCI controllers") extended the xhci-plat driver to support the Armada 375/38x SoCs, mostly by adding a quirk configuring the MBUS window. However, that quirk was run before the clock the controllers needs has been enabled. This usually worked because the clock was first enabled by the bootloader, and left as such until the driver is probe, where it tries to access the MBUS configuration registers before enabling the clock. Things get messy when EPROBE_DEFER is involved during the probe, since as part of its error path, the driver will rightfully disable the clock. When the driver will be reprobed, it will retry to access the MBUS registers, but this time with the clock disabled, which hangs forever. Fix this by running the quirks after the clock has been enabled by the driver. Signed-off-by: Maxime Ripard Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-plat.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 08d402b15482d9..0e11d61408ff3f 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -83,16 +83,6 @@ static int xhci_plat_probe(struct platform_device *pdev) if (irq < 0) return -ENODEV; - - if (of_device_is_compatible(pdev->dev.of_node, - "marvell,armada-375-xhci") || - of_device_is_compatible(pdev->dev.of_node, - "marvell,armada-380-xhci")) { - ret = xhci_mvebu_mbus_init_quirk(pdev); - if (ret) - return ret; - } - /* Initialize dma_mask and coherent_dma_mask to 32-bits */ ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); if (ret) @@ -127,6 +117,15 @@ static int xhci_plat_probe(struct platform_device *pdev) goto put_hcd; } + if (of_device_is_compatible(pdev->dev.of_node, + "marvell,armada-375-xhci") || + of_device_is_compatible(pdev->dev.of_node, + "marvell,armada-380-xhci")) { + ret = xhci_mvebu_mbus_init_quirk(pdev); + if (ret) + goto disable_clk; + } + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) goto disable_clk; From 7f88b0324fa3b93dd80f2f931ddee1d556d6781a Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Tue, 24 Feb 2015 18:27:01 +0200 Subject: [PATCH 268/788] xhci: Allocate correct amount of scratchpad buffers commit 6596a926b0b6c80b730a1dd2fa91908e0a539c37 upstream. Include the high order bit fields for Max scratchpad buffers when calculating how many scratchpad buffers are needed. I'm suprised this hasn't caused more issues, we never allocated more than 32 buffers even if xhci needed more. Either we got lucky and xhci never really used past that area, or then we got enough zeroed dma memory anyway. Should be backported as far back as possible Reported-by: Tim Chen Tested-by: Tim Chen Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index cc7c5bb7cbcf88..be67765a9ece0f 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -88,9 +88,10 @@ struct xhci_cap_regs { #define HCS_IST(p) (((p) >> 0) & 0xf) /* bits 4:7, max number of Event Ring segments */ #define HCS_ERST_MAX(p) (((p) >> 4) & 0xf) +/* bits 21:25 Hi 5 bits of Scratchpad buffers SW must allocate for the HW */ /* bit 26 Scratchpad restore - for save/restore HW state - not used yet */ -/* bits 27:31 number of Scratchpad buffers SW must allocate for the HW */ -#define HCS_MAX_SCRATCHPAD(p) (((p) >> 27) & 0x1f) +/* bits 27:31 Lo 5 bits of Scratchpad buffers SW must allocate for the HW */ +#define HCS_MAX_SCRATCHPAD(p) ((((p) >> 16) & 0x3e0) | (((p) >> 27) & 0x1f)) /* HCSPARAMS3 - hcs_params3 - bitmasks */ /* bits 0:7, Max U1 to U0 latency for the roothub ports */ From 19584ca9b13815ee7e565b8f8ec4bf32a2c2c472 Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Fri, 6 Mar 2015 17:14:21 +0200 Subject: [PATCH 269/788] xhci: fix reporting of 0-sized URBs in control endpoint commit 45ba2154d12fc43b70312198ec47085f10be801a upstream. When a control transfer has a short data stage, the xHCI controller generates two transfer events: a COMP_SHORT_TX event that specifies the untransferred amount, and a COMP_SUCCESS event. But when the data stage is not short, only the COMP_SUCCESS event occurs. Therefore, xhci-hcd must set urb->actual_length to urb->transfer_buffer_length while processing the COMP_SUCCESS event, unless urb->actual_length was set already by a previous COMP_SHORT_TX event. The driver checks this by seeing whether urb->actual_length == 0, but this alone is the wrong test, as it is entirely possible for a short transfer to have an urb->actual_length = 0. This patch changes the xhci driver to rely on a new td->urb_length_set flag, which is set to true when a COMP_SHORT_TX event is received and the URB length updated at that stage. This fixes a bug which affected the HSO plugin, which relies on URBs with urb->actual_length == 0 to halt re-submitting the RX URB in the control endpoint. Signed-off-by: Aleksander Morgado Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 10 ++++++++-- drivers/usb/host/xhci.h | 3 +++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index e692e769c50c56..2a924d500d8a59 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1946,7 +1946,7 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, if (event_trb != ep_ring->dequeue) { /* The event was for the status stage */ if (event_trb == td->last_trb) { - if (td->urb->actual_length != 0) { + if (td->urb_length_set) { /* Don't overwrite a previously set error code */ if ((*status == -EINPROGRESS || *status == 0) && @@ -1960,7 +1960,13 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, td->urb->transfer_buffer_length; } } else { - /* Maybe the event was for the data stage? */ + /* + * Maybe the event was for the data stage? If so, update + * already the actual_length of the URB and flag it as + * set, so that it is not overwritten in the event for + * the last TRB. + */ + td->urb_length_set = true; td->urb->actual_length = td->urb->transfer_buffer_length - EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index be67765a9ece0f..4667d8f0557878 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1,3 +1,4 @@ + /* * xHCI host controller driver * @@ -1289,6 +1290,8 @@ struct xhci_td { struct xhci_segment *start_seg; union xhci_trb *first_trb; union xhci_trb *last_trb; + /* actual_length of the URB has already been set */ + bool urb_length_set; }; /* xHCI command default timeout value */ From f51b8a1bd956b9d0edc995a33c2ced44217643d1 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Fri, 6 Mar 2015 17:23:19 +0200 Subject: [PATCH 270/788] xhci: Workaround for PME stuck issues in Intel xhci commit b8cb91e058cd0c0f02059c1207293c5b31d350fa upstream. The xhci in Intel Sunrisepoint and Cherryview platforms need a driver workaround for a Stuck PME that might either block PME events in suspend, or create spurious PME events preventing runtime suspend. Workaround is to clear a internal PME flag, BIT(28) in a vendor specific PMCTRL register at offset 0x80a4, in both suspend resume callbacks Without this, xhci connected usb devices might never be able to wake up the system from suspend, or prevent device from going to suspend (xhci d3) Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-pci.c | 30 ++++++++++++++++++++++++++++++ drivers/usb/host/xhci.h | 1 + 2 files changed, 31 insertions(+) diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 7f76c8a12f89db..fd53c9ebd662a5 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -37,6 +37,9 @@ #define PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI 0x8c31 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 +#define PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI 0x22b5 +#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI 0xa12f +#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI 0x9d2f static const char hcd_name[] = "xhci_hcd"; @@ -133,6 +136,12 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) pdev->device == PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI) { xhci->quirks |= XHCI_SPURIOUS_REBOOT; } + if (pdev->vendor == PCI_VENDOR_ID_INTEL && + (pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI || + pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI || + pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI)) { + xhci->quirks |= XHCI_PME_STUCK_QUIRK; + } if (pdev->vendor == PCI_VENDOR_ID_ETRON && pdev->device == PCI_DEVICE_ID_EJ168) { xhci->quirks |= XHCI_RESET_ON_RESUME; @@ -159,6 +168,21 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) "QUIRK: Resetting on resume"); } +/* + * Make sure PME works on some Intel xHCI controllers by writing 1 to clear + * the Internal PME flag bit in vendor specific PMCTRL register at offset 0x80a4 + */ +static void xhci_pme_quirk(struct xhci_hcd *xhci) +{ + u32 val; + void __iomem *reg; + + reg = (void __iomem *) xhci->cap_regs + 0x80a4; + val = readl(reg); + writel(val | BIT(28), reg); + readl(reg); +} + /* called during probe() after chip reset completes */ static int xhci_pci_setup(struct usb_hcd *hcd) { @@ -283,6 +307,9 @@ static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) if (xhci->quirks & XHCI_COMP_MODE_QUIRK) pdev->no_d3cold = true; + if (xhci->quirks & XHCI_PME_STUCK_QUIRK) + xhci_pme_quirk(xhci); + return xhci_suspend(xhci, do_wakeup); } @@ -313,6 +340,9 @@ static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated) if (pdev->vendor == PCI_VENDOR_ID_INTEL) usb_enable_intel_xhci_ports(pdev); + if (xhci->quirks & XHCI_PME_STUCK_QUIRK) + xhci_pme_quirk(xhci); + retval = xhci_resume(xhci, hibernated); return retval; } diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 4667d8f0557878..ab09b1ae5f83c0 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1564,6 +1564,7 @@ struct xhci_hcd { #define XHCI_SPURIOUS_WAKEUP (1 << 18) /* For controllers with a broken beyond repair streams implementation */ #define XHCI_BROKEN_STREAMS (1 << 19) +#define XHCI_PME_STUCK_QUIRK (1 << 20) unsigned int num_active_eps; unsigned int limit_active_eps; /* There are two roothubs to keep track of bus suspend info for */ From 5a34f1cb903e43aa39aaab80b68af64e0e3afacc Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 26 Feb 2015 15:50:50 +0200 Subject: [PATCH 271/788] mac80211: Send EAPOL frames at lowest rate commit 9c1c98a3bb7b7593b60264b9a07e001e68b46697 upstream. The current minstrel_ht rate control behavior is somewhat optimistic in trying to find optimum TX rate. While this is usually fine for normal Data frames, there are cases where a more conservative set of retry parameters would be beneficial to make the connection more robust. EAPOL frames are critical to the authentication and especially the EAPOL-Key message 4/4 (the last message in the 4-way handshake) is important to get through to the AP. If that message is lost, the only recovery mechanism in many cases is to reassociate with the AP and start from scratch. This can often be avoided by trying to send the frame with more conservative rate and/or with more link layer retries. In most cases, minstrel_ht is currently using the initial EAPOL-Key frames for probing higher rates and this results in only five link layer transmission attempts (one at high(ish) MCS and four at MCS0). While this works with most APs, it looks like there are some deployed APs that may have issues with the EAPOL frames using HT MCS immediately after association. Similarly, there may be issues in cases where the signal strength or radio environment is not good enough to be able to get frames through even at couple of MCS 0 tries. The best approach for this would likely to be to reduce the TX rate for the last rate (3rd rate parameter in the set) to a low basic rate (say, 6 Mbps on 5 GHz and 2 or 5.5 Mbps on 2.4 GHz), but doing that cleanly requires some more effort. For now, we can start with a simple one-liner that forces the minimum rate to be used for EAPOL frames similarly how the TX rate is selected for the IEEE 802.11 Management frames. This does result in a small extra latency added to the cases where the AP would be able to receive the higher rate, but taken into account how small number of EAPOL frames are used, this is likely to be insignificant. A future optimization in the minstrel_ht design can also allow this patch to be reverted to get back to the more optimized initial TX rate. It should also be noted that many drivers that do not use minstrel as the rate control algorithm are already doing similar workarounds by forcing the lowest TX rate to be used for EAPOL frames. Reported-by: Linus Torvalds Tested-by: Linus Torvalds Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- net/mac80211/tx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 058686a721a1de..097821b1c9cae6 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -566,6 +566,7 @@ ieee80211_tx_h_check_control_port_protocol(struct ieee80211_tx_data *tx) if (tx->sdata->control_port_no_encrypt) info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; info->control.flags |= IEEE80211_TX_CTRL_PORT_CTRL_PROTO; + info->flags |= IEEE80211_TX_CTL_USE_MINRATE; } return TX_CONTINUE; From 8239b52a120cf2e6b7ac6320000dddb4bde507eb Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 16 Dec 2014 16:01:39 +0200 Subject: [PATCH 272/788] mac80211: notify channel switch at the end of ieee80211_chswitch_post_beacon() commit 688b1ecfb9ed0484754d2653386e3c44c58ede5c upstream. The call to cfg80211_ch_switch_notify() should be at the end of the ieee80211_chswitch_post_beacon() function, because it should only be sent if everything succeeded. Fixes: d04b5ac9e70b ("cfg80211/mac80211: allow any interface to send channel switch notifications") Signed-off-by: Luciano Coelho Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- net/mac80211/mlme.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 837a406a9dd67b..bf4b84ff1c7020 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1054,8 +1054,6 @@ static void ieee80211_chswitch_post_beacon(struct ieee80211_sub_if_data *sdata) sdata->csa_block_tx = false; } - cfg80211_ch_switch_notify(sdata->dev, &sdata->reserved_chandef); - sdata->vif.csa_active = false; ifmgd->csa_waiting_bcn = false; @@ -1067,6 +1065,8 @@ static void ieee80211_chswitch_post_beacon(struct ieee80211_sub_if_data *sdata) &ifmgd->csa_connection_drop_work); return; } + + cfg80211_ch_switch_notify(sdata->dev, &sdata->reserved_chandef); } void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success) From a0c9b824b2f38ae9c8a455f8a9c8f6021f7cb905 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 4 Mar 2015 10:39:03 +0100 Subject: [PATCH 273/788] net: irda: fix wait_until_sent poll timeout commit 2c3fbe3cf28fbd7001545a92a83b4f8acfd9fa36 upstream. In case an infinite timeout (0) is requested, the irda wait_until_sent implementation would use a zero poll timeout rather than the default 200ms. Note that wait_until_sent is currently never called with a 0-timeout argument due to a bug in tty_wait_until_sent. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- net/irda/ircomm/ircomm_tty.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c index 40695b9751c10b..4efe486baee62f 100644 --- a/net/irda/ircomm/ircomm_tty.c +++ b/net/irda/ircomm/ircomm_tty.c @@ -798,7 +798,9 @@ static void ircomm_tty_wait_until_sent(struct tty_struct *tty, int timeout) orig_jiffies = jiffies; /* Set poll time to 200 ms */ - poll_time = IRDA_MIN(timeout, msecs_to_jiffies(200)); + poll_time = msecs_to_jiffies(200); + if (timeout) + poll_time = min_t(unsigned long, timeout, poll_time); spin_lock_irqsave(&self->spinlock, flags); while (self->tx_skb && self->tx_skb->len) { From 0385cedcdfc205910ece9200bdd7fa30535f4349 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 4 Mar 2015 10:39:05 +0100 Subject: [PATCH 274/788] USB: serial: fix infinite wait_until_sent timeout commit f528bf4f57e43d1af4b2a5c97f09e43e0338c105 upstream. Make sure to handle an infinite timeout (0). Note that wait_until_sent is currently never called with a 0-timeout argument due to a bug in tty_wait_until_sent. Fixes: dcf010503966 ("USB: serial: add generic wait_until_sent implementation") Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/generic.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index ccf1df7c4b80f3..54e170dd3dad0c 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -258,7 +258,8 @@ void usb_serial_generic_wait_until_sent(struct tty_struct *tty, long timeout) * character or at least one jiffy. */ period = max_t(unsigned long, (10 * HZ / bps), 1); - period = min_t(unsigned long, period, timeout); + if (timeout) + period = min_t(unsigned long, period, timeout); dev_dbg(&port->dev, "%s - timeout = %u ms, period = %u ms\n", __func__, jiffies_to_msecs(timeout), @@ -268,7 +269,7 @@ void usb_serial_generic_wait_until_sent(struct tty_struct *tty, long timeout) schedule_timeout_interruptible(period); if (signal_pending(current)) break; - if (time_after(jiffies, expire)) + if (timeout && time_after(jiffies, expire)) break; } } From c1fdf10bac5b902687c8aa693fe70ff40051b4d3 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 4 Mar 2015 10:39:06 +0100 Subject: [PATCH 275/788] TTY: fix tty_wait_until_sent on 64-bit machines commit 79fbf4a550ed6a22e1ae1516113e6c7fa5d56a53 upstream. Fix overflow bug in tty_wait_until_sent on 64-bit machines, where an infinite timeout (0) would be passed to the underlying tty-driver's wait_until_sent-operation as a negative timeout (-1), causing it to return immediately. This manifests itself for example as tcdrain() returning immediately, drivers not honouring the drain flags when setting terminal attributes, or even dropped data on close as a requested infinite closing-wait timeout would be ignored. The first symptom was reported by Asier LLANO who noted that tcdrain() returned prematurely when using the ftdi_sio usb-serial driver. Fix this by passing 0 rather than MAX_SCHEDULE_TIMEOUT (LONG_MAX) to the underlying tty driver. Note that the serial-core wait_until_sent-implementation is not affected by this bug due to a lucky chance (comparison to an unsigned maximum timeout), and neither is the cyclades one that had an explicit check for negative timeouts, but all other tty drivers appear to be affected. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: ZIV-Asier Llano Palacios Signed-off-by: Johan Hovold Reviewed-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_ioctl.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index 1787fa4d9448bf..552076b25091b9 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -217,11 +217,17 @@ void tty_wait_until_sent(struct tty_struct *tty, long timeout) #endif if (!timeout) timeout = MAX_SCHEDULE_TIMEOUT; + if (wait_event_interruptible_timeout(tty->write_wait, - !tty_chars_in_buffer(tty), timeout) >= 0) { - if (tty->ops->wait_until_sent) - tty->ops->wait_until_sent(tty, timeout); + !tty_chars_in_buffer(tty), timeout) < 0) { + return; } + + if (timeout == MAX_SCHEDULE_TIMEOUT) + timeout = 0; + + if (tty->ops->wait_until_sent) + tty->ops->wait_until_sent(tty, timeout); } EXPORT_SYMBOL(tty_wait_until_sent); From 0db70c6d0c0d782ea2959278723a3d30bfa92de0 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 18 Feb 2015 10:34:50 +0700 Subject: [PATCH 276/788] USB: serial: fix potential use-after-free after failed probe commit 07fdfc5e9f1c966be8722e8fa927e5ea140df5ce upstream. Fix return value in probe error path, which could end up returning success (0) on errors. This could in turn lead to use-after-free or double free (e.g. in port_remove) when the port device is removed. Fixes: c706ebdfc895 ("USB: usb-serial: call port_probe and port_remove at the right times") Signed-off-by: Johan Hovold Acked-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c index 9374bd2aba2075..5d8d86666b901d 100644 --- a/drivers/usb/serial/bus.c +++ b/drivers/usb/serial/bus.c @@ -75,7 +75,7 @@ static int usb_serial_device_probe(struct device *dev) retval = device_create_file(dev, &dev_attr_port_number); if (retval) { if (driver->port_remove) - retval = driver->port_remove(port); + driver->port_remove(port); goto exit_with_autopm; } From a2cfb7dc7d236003bd8631f61a56a1d26d4b3b9c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 18 Feb 2015 10:34:51 +0700 Subject: [PATCH 277/788] USB: serial: fix tty-device error handling at probe commit ca4383a3947a83286bc9b9c598a1f55e867871d7 upstream. Add missing error handling when registering the tty device at port probe. This avoids trying to remove an uninitialised character device when the port device is removed. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: Takashi Iwai Signed-off-by: Johan Hovold Acked-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/bus.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c index 5d8d86666b901d..6f91eb9ae81a44 100644 --- a/drivers/usb/serial/bus.c +++ b/drivers/usb/serial/bus.c @@ -51,6 +51,7 @@ static int usb_serial_device_probe(struct device *dev) { struct usb_serial_driver *driver; struct usb_serial_port *port; + struct device *tty_dev; int retval = 0; int minor; @@ -80,7 +81,15 @@ static int usb_serial_device_probe(struct device *dev) } minor = port->minor; - tty_register_device(usb_serial_tty_driver, minor, dev); + tty_dev = tty_register_device(usb_serial_tty_driver, minor, dev); + if (IS_ERR(tty_dev)) { + retval = PTR_ERR(tty_dev); + device_remove_file(dev, &dev_attr_port_number); + if (driver->port_remove) + driver->port_remove(port); + goto exit_with_autopm; + } + dev_info(&port->serial->dev->dev, "%s converter now attached to ttyUSB%d\n", driver->description, minor); From a2f96bd06255a75ecae881adff82ac7eed226866 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 21 Feb 2015 22:19:57 -0500 Subject: [PATCH 278/788] autofs4 copy_dev_ioctl(): keep the value of ->size we'd used for allocation commit 0a280962dc6e117e0e4baa668453f753579265d9 upstream. X-Coverup: just ask spender Signed-off-by: Al Viro Signed-off-by: Greg Kroah-Hartman --- fs/autofs4/dev-ioctl.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/autofs4/dev-ioctl.c b/fs/autofs4/dev-ioctl.c index aaf96cb25452cf..ac7d921ed9844b 100644 --- a/fs/autofs4/dev-ioctl.c +++ b/fs/autofs4/dev-ioctl.c @@ -95,7 +95,7 @@ static int check_dev_ioctl_version(int cmd, struct autofs_dev_ioctl *param) */ static struct autofs_dev_ioctl *copy_dev_ioctl(struct autofs_dev_ioctl __user *in) { - struct autofs_dev_ioctl tmp; + struct autofs_dev_ioctl tmp, *res; if (copy_from_user(&tmp, in, sizeof(tmp))) return ERR_PTR(-EFAULT); @@ -106,7 +106,11 @@ static struct autofs_dev_ioctl *copy_dev_ioctl(struct autofs_dev_ioctl __user *i if (tmp.size > (PATH_MAX + sizeof(tmp))) return ERR_PTR(-ENAMETOOLONG); - return memdup_user(in, tmp.size); + res = memdup_user(in, tmp.size); + if (!IS_ERR(res)) + res->size = tmp.size; + + return res; } static inline void free_dev_ioctl(struct autofs_dev_ioctl *param) From 17a75de122bc850b3570d08757840f6798c113a3 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Fri, 6 Feb 2015 16:28:17 +0100 Subject: [PATCH 279/788] autofs4: Wrong format for printing dentry commit 76bf3f6b1d6ac4c770bb121b0461c460aa068e64 upstream. %pD for struct file*, %pd for struct dentry*. Fixes: a455589f181e ("assorted conversions to %p[dD]") Signed-off-by: Rasmus Villemoes Signed-off-by: Al Viro Signed-off-by: Greg Kroah-Hartman --- fs/autofs4/root.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index dbb5b7212ce162..7ba355b8d4acd8 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -108,7 +108,7 @@ static int autofs4_dir_open(struct inode *inode, struct file *file) struct dentry *dentry = file->f_path.dentry; struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); - DPRINTK("file=%p dentry=%p %pD", file, dentry, dentry); + DPRINTK("file=%p dentry=%p %pd", file, dentry, dentry); if (autofs4_oz_mode(sbi)) goto out; From ea149d39ea943c2022b2389faab508bc02363b52 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 21 Feb 2015 22:05:11 -0500 Subject: [PATCH 280/788] debugfs: leave freeing a symlink body until inode eviction commit 0db59e59299f0b67450c5db21f7f316c8fb04e84 upstream. As it is, we have debugfs_remove() racing with symlink traversals. Supply ->evict_inode() and do freeing there - inode will remain pinned until we are done with the symlink body. And rip the idiocy with checking if dentry is positive right after we'd verified debugfs_positive(), which is a stronger check... Signed-off-by: Al Viro Signed-off-by: Greg Kroah-Hartman --- fs/debugfs/inode.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index 05f2960ed7c3be..6f0ce531e2211a 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -246,10 +246,19 @@ static int debugfs_show_options(struct seq_file *m, struct dentry *root) return 0; } +static void debugfs_evict_inode(struct inode *inode) +{ + truncate_inode_pages_final(&inode->i_data); + clear_inode(inode); + if (S_ISLNK(inode->i_mode)) + kfree(inode->i_private); +} + static const struct super_operations debugfs_super_operations = { .statfs = simple_statfs, .remount_fs = debugfs_remount, .show_options = debugfs_show_options, + .evict_inode = debugfs_evict_inode, }; static int debug_fill_super(struct super_block *sb, void *data, int silent) @@ -466,23 +475,14 @@ static int __debugfs_remove(struct dentry *dentry, struct dentry *parent) int ret = 0; if (debugfs_positive(dentry)) { - if (dentry->d_inode) { - dget(dentry); - switch (dentry->d_inode->i_mode & S_IFMT) { - case S_IFDIR: - ret = simple_rmdir(parent->d_inode, dentry); - break; - case S_IFLNK: - kfree(dentry->d_inode->i_private); - /* fall through */ - default: - simple_unlink(parent->d_inode, dentry); - break; - } - if (!ret) - d_delete(dentry); - dput(dentry); - } + dget(dentry); + if (S_ISDIR(dentry->d_inode->i_mode)) + ret = simple_rmdir(parent->d_inode, dentry); + else + simple_unlink(parent->d_inode, dentry); + if (!ret) + d_delete(dentry); + dput(dentry); } return ret; } From e95e69b89481deafaed9cf98fa1c7ae0c716d82c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 21 Feb 2015 22:16:11 -0500 Subject: [PATCH 281/788] procfs: fix race between symlink removals and traversals commit 7e0e953bb0cf649f93277ac8fb67ecbb7f7b04a9 upstream. use_pde()/unuse_pde() in ->follow_link()/->put_link() resp. Signed-off-by: Al Viro Signed-off-by: Greg Kroah-Hartman --- fs/proc/generic.c | 12 ------------ fs/proc/inode.c | 21 +++++++++++++++++++++ fs/proc/internal.h | 1 + 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 7fea13229f3370..b502bba0f9fddc 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -223,17 +222,6 @@ void proc_free_inum(unsigned int inum) spin_unlock_irqrestore(&proc_inum_lock, flags); } -static void *proc_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - nd_set_link(nd, __PDE_DATA(dentry->d_inode)); - return NULL; -} - -static const struct inode_operations proc_link_inode_operations = { - .readlink = generic_readlink, - .follow_link = proc_follow_link, -}; - /* * Don't create negative dentries here, return -ENOENT by hand * instead. diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 8420a2f8081182..3b0f8384ab216e 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -23,6 +23,7 @@ #include #include #include +#include #include @@ -393,6 +394,26 @@ static const struct file_operations proc_reg_file_ops_no_compat = { }; #endif +static void *proc_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + struct proc_dir_entry *pde = PDE(dentry->d_inode); + if (unlikely(!use_pde(pde))) + return ERR_PTR(-EINVAL); + nd_set_link(nd, pde->data); + return pde; +} + +static void proc_put_link(struct dentry *dentry, struct nameidata *nd, void *p) +{ + unuse_pde(p); +} + +const struct inode_operations proc_link_inode_operations = { + .readlink = generic_readlink, + .follow_link = proc_follow_link, + .put_link = proc_put_link, +}; + struct inode *proc_get_inode(struct super_block *sb, struct proc_dir_entry *de) { struct inode *inode = new_inode_pseudo(sb); diff --git a/fs/proc/internal.h b/fs/proc/internal.h index 6fcdba573e0fa2..c835b94c0cd3af 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -200,6 +200,7 @@ struct pde_opener { int closing; struct completion *c; }; +extern const struct inode_operations proc_link_inode_operations; extern const struct inode_operations proc_pid_link_inode_operations; From 202935e63a83d8295f74da296e255a8bfcb2c209 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 7 Mar 2015 21:08:46 +0000 Subject: [PATCH 282/788] sunrpc: fix braino in ->poll() commit 1711fd9addf214823b993468567cab1f8254fc51 upstream. POLL_OUT isn't what callers of ->poll() are expecting to see; it's actually __SI_POLL | 2 and it's a siginfo code, not a poll bitmap bit... Signed-off-by: Al Viro Cc: Bruce Fields Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- net/sunrpc/cache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index 33fb105d435262..5199bb1a017e47 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -921,7 +921,7 @@ static unsigned int cache_poll(struct file *filp, poll_table *wait, poll_wait(filp, &queue_wait, wait); /* alway allow write */ - mask = POLL_OUT | POLLWRNORM; + mask = POLLOUT | POLLWRNORM; if (!rp) return mask; From 7fed3a7947cc3954189dfd99baa7e5a48b760040 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 13 Feb 2015 13:08:25 -0500 Subject: [PATCH 283/788] SUNRPC: Always manipulate rpc_rqst::rq_bc_pa_list under xprt->bc_pa_lock commit 813b00d63f6ca1ed40a2f4f9c034d59bc424025e upstream. Other code that accesses rq_bc_pa_list holds xprt->bc_pa_lock. xprt_complete_bc_request() should do the same. Fixes: 2ea24497a1b3 ("SUNRPC: RPC callbacks may be split . . .") Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust Signed-off-by: Greg Kroah-Hartman --- net/sunrpc/backchannel_rqst.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/sunrpc/backchannel_rqst.c b/net/sunrpc/backchannel_rqst.c index 651f49ab601fbd..9dd0ea8db463ac 100644 --- a/net/sunrpc/backchannel_rqst.c +++ b/net/sunrpc/backchannel_rqst.c @@ -309,12 +309,15 @@ void xprt_complete_bc_request(struct rpc_rqst *req, uint32_t copied) struct rpc_xprt *xprt = req->rq_xprt; struct svc_serv *bc_serv = xprt->bc_serv; + spin_lock(&xprt->bc_pa_lock); + list_del(&req->rq_bc_pa_list); + spin_unlock(&xprt->bc_pa_lock); + req->rq_private_buf.len = copied; set_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state); dprintk("RPC: add callback request to list\n"); spin_lock(&bc_serv->sv_cb_lock); - list_del(&req->rq_bc_pa_list); list_add(&req->rq_bc_list, &bc_serv->sv_cb_list); wake_up(&bc_serv->sv_cb_waitq); spin_unlock(&bc_serv->sv_cb_lock); From 14e96578f09632cdad32bc5eb0891acb26c4dbec Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 27 Feb 2015 10:39:17 +0530 Subject: [PATCH 284/788] ARC: Fix KSTK_ESP() commit 13648b0118a24f4fc76c34e6c7b6ccf447e46a2a upstream. /proc//maps currently don't annotate stack vma with "[stack]" This is because KSTK_ESP ie expected to return usermode SP of tsk while currently it returns the kernel mode SP of a sleeping tsk. While the fix is trivial, we also need to adjust the ARC kernel stack unwinder to not use KSTK_SP and friends any more. Reported-and-suggested-by: Alexey Brodkin Signed-off-by: Vineet Gupta Signed-off-by: Greg Kroah-Hartman --- arch/arc/include/asm/processor.h | 9 +++++---- arch/arc/kernel/stacktrace.c | 6 +++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/arch/arc/include/asm/processor.h b/arch/arc/include/asm/processor.h index 210fe97464c386..c750af161979ac 100644 --- a/arch/arc/include/asm/processor.h +++ b/arch/arc/include/asm/processor.h @@ -75,18 +75,19 @@ unsigned long thread_saved_pc(struct task_struct *t); #define release_segments(mm) do { } while (0) #define KSTK_EIP(tsk) (task_pt_regs(tsk)->ret) +#define KSTK_ESP(tsk) (task_pt_regs(tsk)->sp) /* * Where abouts of Task's sp, fp, blink when it was last seen in kernel mode. * Look in process.c for details of kernel stack layout */ -#define KSTK_ESP(tsk) (tsk->thread.ksp) +#define TSK_K_ESP(tsk) (tsk->thread.ksp) -#define KSTK_REG(tsk, off) (*((unsigned int *)(KSTK_ESP(tsk) + \ +#define TSK_K_REG(tsk, off) (*((unsigned int *)(TSK_K_ESP(tsk) + \ sizeof(struct callee_regs) + off))) -#define KSTK_BLINK(tsk) KSTK_REG(tsk, 4) -#define KSTK_FP(tsk) KSTK_REG(tsk, 0) +#define TSK_K_BLINK(tsk) TSK_K_REG(tsk, 4) +#define TSK_K_FP(tsk) TSK_K_REG(tsk, 0) extern void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long usp); diff --git a/arch/arc/kernel/stacktrace.c b/arch/arc/kernel/stacktrace.c index 9ce47cfe23037f..fb98769b6a985d 100644 --- a/arch/arc/kernel/stacktrace.c +++ b/arch/arc/kernel/stacktrace.c @@ -64,9 +64,9 @@ static void seed_unwind_frame_info(struct task_struct *tsk, frame_info->task = tsk; - frame_info->regs.r27 = KSTK_FP(tsk); - frame_info->regs.r28 = KSTK_ESP(tsk); - frame_info->regs.r31 = KSTK_BLINK(tsk); + frame_info->regs.r27 = TSK_K_FP(tsk); + frame_info->regs.r28 = TSK_K_ESP(tsk); + frame_info->regs.r31 = TSK_K_BLINK(tsk); frame_info->regs.r63 = (unsigned int)__switch_to; /* In the prologue of __switch_to, first FP is saved on stack From c6656cf2fd5d654291931fa2afc5d06b61597aa2 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Fri, 27 Feb 2015 18:40:31 +0100 Subject: [PATCH 285/788] tty: fix up atime/mtime mess, take four commit f0bf0bd07943bfde8f5ac39a32664810a379c7d3 upstream. This problem was taken care of three times already in * b0de59b5733d18b0d1974a060860a8b5c1b36a2e (TTY: do not update atime/mtime on read/write), * 37b7f3c76595e23257f61bd80b223de8658617ee (TTY: fix atime/mtime regression), and * b0b885657b6c8ef63a46bc9299b2a7715d19acde (tty: fix up atime/mtime mess, take three) But it still misses one point. As John Paul correctly points out, we do not care about setting date. If somebody ever changes wall time backwards (by mistake for example), tty timestamps are never updated until the original wall time passes. So check the absolute difference of times and if it large than "8 seconds or so", always update the time. That means we will update immediatelly when changing time. Ergo, CAP_SYS_TIME can foul the check, but it was always that way. Thanks John for serving me this so nicely debugged. Signed-off-by: Jiri Slaby Reported-by: John Paul Perry Acked-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 51f066aa375e64..2bb4dfc0287340 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1028,8 +1028,8 @@ EXPORT_SYMBOL(start_tty); /* We limit tty time update visibility to every 8 seconds or so. */ static void tty_update_time(struct timespec *time) { - unsigned long sec = get_seconds() & ~7; - if ((long)(sec - time->tv_sec) > 0) + unsigned long sec = get_seconds(); + if (abs(sec - time->tv_sec) & ~7) time->tv_sec = sec; } From fb1ca99e7f6b8edaef78cfb2eca6d215cc8765a9 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Sun, 15 Feb 2015 18:32:16 +0100 Subject: [PATCH 286/788] serial: 8250: Revert "tty: serial: 8250_core: read only RX if there is something in the FIFO" commit ca8bb4aefb932e3da105f28cbfba36d57a931081 upstream. This reverts commit 0aa525d11859c1a4d5b78fdc704148e2ae03ae13. The conditional RX-FIFO read seems to cause spurious interrupts and we see just: |serial8250: too much work for irq29 The previous behaviour was "default" for decades and Marvell's 88f6282 SoC might not be the only that relies on it. Therefore the Omap fix is reverted for now. Fixes: 0aa525d11859 ("tty: serial: 8250_core: read only RX if there is something in the FIFO") Reported-By: Nicolas Schichan Debuged-By: Peter Hurley Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 11c66856ba2fcb..ea1a8dabd5dc8a 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -2107,8 +2107,8 @@ int serial8250_do_startup(struct uart_port *port) /* * Clear the interrupt registers. */ - if (serial_port_in(port, UART_LSR) & UART_LSR_DR) - serial_port_in(port, UART_RX); + serial_port_in(port, UART_LSR); + serial_port_in(port, UART_RX); serial_port_in(port, UART_IIR); serial_port_in(port, UART_MSR); @@ -2269,8 +2269,8 @@ int serial8250_do_startup(struct uart_port *port) * saved flags to avoid getting false values from polling * routines or the previous session. */ - if (serial_port_in(port, UART_LSR) & UART_LSR_DR) - serial_port_in(port, UART_RX); + serial_port_in(port, UART_LSR); + serial_port_in(port, UART_RX); serial_port_in(port, UART_IIR); serial_port_in(port, UART_MSR); up->lsr_saved_flags = 0; @@ -2363,8 +2363,7 @@ void serial8250_do_shutdown(struct uart_port *port) * Read data port to reset things, and then unlink from * the IRQ chain. */ - if (serial_port_in(port, UART_LSR) & UART_LSR_DR) - serial_port_in(port, UART_RX); + serial_port_in(port, UART_RX); serial8250_rpm_put(up); del_timer_sync(&up->timer); From 39bba749fa0219e1dd6925525eb51ee77794f72f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 18 Dec 2014 10:02:41 +0100 Subject: [PATCH 287/788] ALSA: pcm: Don't leave PREPARED state after draining commit 70372a7566b5e552dbe48abdac08c275081d8558 upstream. When a PCM draining is performed to an empty stream that has been already in PREPARED state, the current code just ignores and leaves as it is, although the drain is supposed to set all such streams to SETUP state. This patch covers that overlooked case. Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/pcm_native.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 095d9572ad2b3a..64d9863d656526 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -1546,6 +1546,8 @@ static int snd_pcm_do_drain_init(struct snd_pcm_substream *substream, int state) if (! snd_pcm_playback_empty(substream)) { snd_pcm_do_start(substream, SNDRV_PCM_STATE_DRAINING); snd_pcm_post_start(substream, SNDRV_PCM_STATE_DRAINING); + } else { + runtime->status->state = SNDRV_PCM_STATE_SETUP; } break; case SNDRV_PCM_STATE_RUNNING: From e820f2b245ca9003589e9bddf768c0d068289c94 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 19 Feb 2015 13:01:37 +0100 Subject: [PATCH 288/788] ALSA: hda - Add pin configs for ASUS mobo with IDT 92HD73XX codec commit 6426460e5d87810e042962281fe3c1e8fc256162 upstream. BIOS doesn't seem to set up pins for 5.1 and the SPDIF out, so we need to give explicitly here. Reported-and-tested-by: Misan Thropos Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_sigmatel.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 6d36c5b7880504..87eff3173ce924 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -79,6 +79,7 @@ enum { STAC_ALIENWARE_M17X, STAC_92HD89XX_HP_FRONT_JACK, STAC_92HD89XX_HP_Z1_G2_RIGHT_MIC_JACK, + STAC_92HD73XX_ASUS_MOBO, STAC_92HD73XX_MODELS }; @@ -1911,7 +1912,18 @@ static const struct hda_fixup stac92hd73xx_fixups[] = { [STAC_92HD89XX_HP_Z1_G2_RIGHT_MIC_JACK] = { .type = HDA_FIXUP_PINS, .v.pins = stac92hd89xx_hp_z1_g2_right_mic_jack_pin_configs, - } + }, + [STAC_92HD73XX_ASUS_MOBO] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + /* enable 5.1 and SPDIF out */ + { 0x0c, 0x01014411 }, + { 0x0d, 0x01014410 }, + { 0x0e, 0x01014412 }, + { 0x22, 0x014b1180 }, + { } + } + }, }; static const struct hda_model_fixup stac92hd73xx_models[] = { @@ -1923,6 +1935,7 @@ static const struct hda_model_fixup stac92hd73xx_models[] = { { .id = STAC_DELL_M6_BOTH, .name = "dell-m6" }, { .id = STAC_DELL_EQ, .name = "dell-eq" }, { .id = STAC_ALIENWARE_M17X, .name = "alienware" }, + { .id = STAC_92HD73XX_ASUS_MOBO, .name = "asus-mobo" }, {} }; @@ -1975,6 +1988,8 @@ static const struct snd_pci_quirk stac92hd73xx_fixup_tbl[] = { "HP Z1 G2", STAC_92HD89XX_HP_Z1_G2_RIGHT_MIC_JACK), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2b17, "unknown HP", STAC_92HD89XX_HP_FRONT_JACK), + SND_PCI_QUIRK(PCI_VENDOR_ID_ASUSTEK, 0x83f8, "ASUS AT4NM10", + STAC_92HD73XX_ASUS_MOBO), {} /* terminator */ }; From 2a5d626d0c627edd44347b0c55a7726faef95a3c Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 21 Feb 2015 23:54:57 +0900 Subject: [PATCH 289/788] ALSA: fireworks/bebob/dice/oxfw: add reference-counting for FireWire unit commit 12ed719291a953d443921f9cdb0ffee41066c340 upstream. Fireworks and Dice drivers try to touch instances of FireWire unit after sound card object is released, while references to the unit is decremented in .remove(). When unplugging during streaming, sound card object is released after .remove(), thus Fireworks and Dice drivers causes GPF or Null-pointer-dereferencing to application processes because an instance of FireWire unit was already released. This commit adds reference-counting for FireWire unit in drivers to allow them to touch an instance of FireWire unit after .remove(). In most case, any operations after .remove() may be failed safely. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/firewire/bebob/bebob.c | 12 +++++++++++- sound/firewire/dice/dice.c | 11 ++++++++++- sound/firewire/fireworks/fireworks.c | 12 +++++++++++- sound/firewire/oxfw/oxfw.c | 11 ++++++++++- 4 files changed, 42 insertions(+), 4 deletions(-) diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c index fc19c99654aa02..b612599fb543ea 100644 --- a/sound/firewire/bebob/bebob.c +++ b/sound/firewire/bebob/bebob.c @@ -116,11 +116,19 @@ name_device(struct snd_bebob *bebob, unsigned int vendor_id) return err; } +/* + * This module releases the FireWire unit data after all ALSA character devices + * are released by applications. This is for releasing stream data or finishing + * transactions safely. Thus at returning from .remove(), this module still keep + * references for the unit. + */ static void bebob_card_free(struct snd_card *card) { struct snd_bebob *bebob = card->private_data; + fw_unit_put(bebob->unit); + if (bebob->card_index >= 0) { mutex_lock(&devices_mutex); clear_bit(bebob->card_index, devices_used); @@ -205,7 +213,7 @@ bebob_probe(struct fw_unit *unit, card->private_free = bebob_card_free; bebob->card = card; - bebob->unit = unit; + bebob->unit = fw_unit_get(unit); bebob->spec = spec; mutex_init(&bebob->mutex); spin_lock_init(&bebob->lock); @@ -310,6 +318,8 @@ static void bebob_remove(struct fw_unit *unit) snd_bebob_stream_destroy_duplex(bebob); snd_card_disconnect(bebob->card); + + /* No need to wait for releasing card object in this context. */ snd_card_free_when_closed(bebob->card); } diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index 90d8f40ff72712..797f0726fc7446 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -226,11 +226,19 @@ static void dice_card_strings(struct snd_dice *dice) strcpy(card->mixername, "DICE"); } +/* + * This module releases the FireWire unit data after all ALSA character devices + * are released by applications. This is for releasing stream data or finishing + * transactions safely. Thus at returning from .remove(), this module still keep + * references for the unit. + */ static void dice_card_free(struct snd_card *card) { struct snd_dice *dice = card->private_data; snd_dice_transaction_destroy(dice); + fw_unit_put(dice->unit); + mutex_destroy(&dice->mutex); } @@ -251,7 +259,7 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) dice = card->private_data; dice->card = card; - dice->unit = unit; + dice->unit = fw_unit_get(unit); card->private_free = dice_card_free; spin_lock_init(&dice->lock); @@ -309,6 +317,7 @@ static void dice_remove(struct fw_unit *unit) snd_dice_stream_destroy_duplex(dice); + /* No need to wait for releasing card object in this context. */ snd_card_free_when_closed(dice->card); } diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c index 3e2ed8e82cbc49..1e33394d8a930e 100644 --- a/sound/firewire/fireworks/fireworks.c +++ b/sound/firewire/fireworks/fireworks.c @@ -173,11 +173,19 @@ get_hardware_info(struct snd_efw *efw) return err; } +/* + * This module releases the FireWire unit data after all ALSA character devices + * are released by applications. This is for releasing stream data or finishing + * transactions safely. Thus at returning from .remove(), this module still keep + * references for the unit. + */ static void efw_card_free(struct snd_card *card) { struct snd_efw *efw = card->private_data; + fw_unit_put(efw->unit); + if (efw->card_index >= 0) { mutex_lock(&devices_mutex); clear_bit(efw->card_index, devices_used); @@ -218,7 +226,7 @@ efw_probe(struct fw_unit *unit, card->private_free = efw_card_free; efw->card = card; - efw->unit = unit; + efw->unit = fw_unit_get(unit); mutex_init(&efw->mutex); spin_lock_init(&efw->lock); init_waitqueue_head(&efw->hwdep_wait); @@ -293,6 +301,8 @@ static void efw_remove(struct fw_unit *unit) snd_efw_transaction_remove_instance(efw); snd_card_disconnect(efw->card); + + /* No need to wait for releasing card object in this context. */ snd_card_free_when_closed(efw->card); } diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c index 60e5cad0531aeb..1607b26404c380 100644 --- a/sound/firewire/oxfw/oxfw.c +++ b/sound/firewire/oxfw/oxfw.c @@ -104,11 +104,19 @@ static int name_card(struct snd_oxfw *oxfw) return err; } +/* + * This module releases the FireWire unit data after all ALSA character devices + * are released by applications. This is for releasing stream data or finishing + * transactions safely. Thus at returning from .remove(), this module still keep + * references for the unit. + */ static void oxfw_card_free(struct snd_card *card) { struct snd_oxfw *oxfw = card->private_data; unsigned int i; + fw_unit_put(oxfw->unit); + for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { kfree(oxfw->tx_stream_formats[i]); kfree(oxfw->rx_stream_formats[i]); @@ -136,7 +144,7 @@ static int oxfw_probe(struct fw_unit *unit, oxfw = card->private_data; oxfw->card = card; mutex_init(&oxfw->mutex); - oxfw->unit = unit; + oxfw->unit = fw_unit_get(unit); oxfw->device_info = (const struct device_info *)id->driver_data; spin_lock_init(&oxfw->lock); init_waitqueue_head(&oxfw->hwdep_wait); @@ -218,6 +226,7 @@ static void oxfw_remove(struct fw_unit *unit) if (oxfw->has_output) snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream); + /* No need to wait for releasing card object in this context. */ snd_card_free_when_closed(oxfw->card); } From c1972576af1db89251965ab266fd05aa58ff8e73 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 21 Feb 2015 23:54:58 +0900 Subject: [PATCH 290/788] ALSA: firewire-lib: remove reference counting commit c6f224dc20ad959175c2dfec70b5a61c6503a793 upstream. AMDTP helper functions increment/decrement reference counter for an instance of FireWire unit, while it's complicated for each driver to process error state. In previous commit, each driver has the role of reference counting. This commit removes this role from the helper function. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/firewire/amdtp.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 0d580186ef1ac3..911341b2a897b7 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -78,7 +78,7 @@ static void pcm_period_tasklet(unsigned long data); int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir, enum cip_flags flags) { - s->unit = fw_unit_get(unit); + s->unit = unit; s->direction = dir; s->flags = flags; s->context = ERR_PTR(-1); @@ -102,7 +102,6 @@ void amdtp_stream_destroy(struct amdtp_stream *s) { WARN_ON(amdtp_stream_running(s)); mutex_destroy(&s->mutex); - fw_unit_put(s->unit); } EXPORT_SYMBOL(amdtp_stream_destroy); From 7e6dddf2532e416d6fb10f1ca86319e45a534cba Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 21 Feb 2015 23:54:59 +0900 Subject: [PATCH 291/788] ALSA: fireworks/bebob/dice/oxfw: allow stream destructor after releasing runtime commit d23c2cc4485d10f0cedfef99dd2961d9652b1b3f upstream. Currently stream destructor in each driver has a problem to be called in a context in which sound card object is released, because the destructors call amdtp_stream_pcm_abort() and touch PCM runtime data. The PCM runtime data is destroyed in application's context with snd_pcm_close(), on the other hand PCM substream data is destroyed after sound card object is released, in most case after all of ALSA character devices are released. When PCM runtime is destroyed and PCM substream is remained, amdtp_stream_pcm_abort() touches PCM runtime data and causes Null-pointer-dereference. This commit changes stream destructors and allows each driver to call it after releasing runtime. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/firewire/bebob/bebob_stream.c | 12 ++++-------- sound/firewire/dice/dice-stream.c | 18 ++++++++++++------ sound/firewire/fireworks/fireworks_stream.c | 15 ++++++++++----- sound/firewire/oxfw/oxfw-stream.c | 6 ++++-- 4 files changed, 30 insertions(+), 21 deletions(-) diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index 0ebcabfdc7ce01..fcca3eebc91fb5 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -410,8 +410,6 @@ break_both_connections(struct snd_bebob *bebob) static void destroy_both_connections(struct snd_bebob *bebob) { - break_both_connections(bebob); - cmp_connection_destroy(&bebob->in_conn); cmp_connection_destroy(&bebob->out_conn); } @@ -712,16 +710,14 @@ void snd_bebob_stream_update_duplex(struct snd_bebob *bebob) mutex_unlock(&bebob->mutex); } +/* + * This function should be called before starting streams or after stopping + * streams. + */ void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob) { mutex_lock(&bebob->mutex); - amdtp_stream_pcm_abort(&bebob->rx_stream); - amdtp_stream_pcm_abort(&bebob->tx_stream); - - amdtp_stream_stop(&bebob->rx_stream); - amdtp_stream_stop(&bebob->tx_stream); - amdtp_stream_destroy(&bebob->rx_stream); amdtp_stream_destroy(&bebob->tx_stream); diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index fa9cf761b610ad..07dbd01d7a6bd3 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -311,14 +311,21 @@ static int init_stream(struct snd_dice *dice, struct amdtp_stream *stream) return err; } +/* + * This function should be called before starting streams or after stopping + * streams. + */ static void destroy_stream(struct snd_dice *dice, struct amdtp_stream *stream) { - amdtp_stream_destroy(stream); + struct fw_iso_resources *resources; if (stream == &dice->tx_stream) - fw_iso_resources_destroy(&dice->tx_resources); + resources = &dice->tx_resources; else - fw_iso_resources_destroy(&dice->rx_resources); + resources = &dice->rx_resources; + + amdtp_stream_destroy(stream); + fw_iso_resources_destroy(resources); } int snd_dice_stream_init_duplex(struct snd_dice *dice) @@ -332,6 +339,8 @@ int snd_dice_stream_init_duplex(struct snd_dice *dice) goto end; err = init_stream(dice, &dice->rx_stream); + if (err < 0) + destroy_stream(dice, &dice->tx_stream); end: return err; } @@ -340,10 +349,7 @@ void snd_dice_stream_destroy_duplex(struct snd_dice *dice) { snd_dice_transaction_clear_enable(dice); - stop_stream(dice, &dice->tx_stream); destroy_stream(dice, &dice->tx_stream); - - stop_stream(dice, &dice->rx_stream); destroy_stream(dice, &dice->rx_stream); dice->substreams_counter = 0; diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c index 4f440e16366780..f817b7ae097e28 100644 --- a/sound/firewire/fireworks/fireworks_stream.c +++ b/sound/firewire/fireworks/fireworks_stream.c @@ -100,17 +100,22 @@ start_stream(struct snd_efw *efw, struct amdtp_stream *stream, return err; } +/* + * This function should be called before starting the stream or after stopping + * the streams. + */ static void destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream) { - stop_stream(efw, stream); - - amdtp_stream_destroy(stream); + struct cmp_connection *conn; if (stream == &efw->tx_stream) - cmp_connection_destroy(&efw->out_conn); + conn = &efw->out_conn; else - cmp_connection_destroy(&efw->in_conn); + conn = &efw->in_conn; + + amdtp_stream_destroy(stream); + cmp_connection_destroy(&efw->out_conn); } static int diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c index bda845afb47070..29ccb3637164f8 100644 --- a/sound/firewire/oxfw/oxfw-stream.c +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -337,6 +337,10 @@ void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw, stop_stream(oxfw, stream); } +/* + * This function should be called before starting the stream or after stopping + * the streams. + */ void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw, struct amdtp_stream *stream) { @@ -347,8 +351,6 @@ void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw, else conn = &oxfw->in_conn; - stop_stream(oxfw, stream); - amdtp_stream_destroy(stream); cmp_connection_destroy(conn); } From 547eea7bd46cb4551273bee0c775d4da5c62d5a5 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 21 Feb 2015 23:55:00 +0900 Subject: [PATCH 292/788] ALSA: fireworks/bebob/dice/oxfw: make it possible to shutdown safely commit dec84316dd53c90e93b4ee849483bd4bd1e9a585 upstream. A part of these drivers, especially BeBoB driver, are programmed to wait some events. Thus the drivers should not destroy any data in .remove() context. This commit moves some destructors from 'struct fw_driver.remove()' to 'struct snd_card.private_free()' to shutdown safely. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/firewire/bebob/bebob.c | 10 ++++++---- sound/firewire/bebob/bebob_stream.c | 4 ---- sound/firewire/dice/dice.c | 5 +---- sound/firewire/fireworks/fireworks.c | 10 ++++------ sound/firewire/fireworks/fireworks_stream.c | 4 ---- sound/firewire/oxfw/oxfw.c | 10 ++++------ 6 files changed, 15 insertions(+), 28 deletions(-) diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c index b612599fb543ea..611b7dae7ee54c 100644 --- a/sound/firewire/bebob/bebob.c +++ b/sound/firewire/bebob/bebob.c @@ -127,8 +127,11 @@ bebob_card_free(struct snd_card *card) { struct snd_bebob *bebob = card->private_data; + snd_bebob_stream_destroy_duplex(bebob); fw_unit_put(bebob->unit); + kfree(bebob->maudio_special_quirk); + if (bebob->card_index >= 0) { mutex_lock(&devices_mutex); clear_bit(bebob->card_index, devices_used); @@ -314,10 +317,9 @@ static void bebob_remove(struct fw_unit *unit) if (bebob == NULL) return; - kfree(bebob->maudio_special_quirk); - - snd_bebob_stream_destroy_duplex(bebob); - snd_card_disconnect(bebob->card); + /* Awake bus-reset waiters. */ + if (!completion_done(&bebob->bus_reset)) + complete_all(&bebob->bus_reset); /* No need to wait for releasing card object in this context. */ snd_card_free_when_closed(bebob->card); diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index fcca3eebc91fb5..98e4fc8121a1f4 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -716,14 +716,10 @@ void snd_bebob_stream_update_duplex(struct snd_bebob *bebob) */ void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob) { - mutex_lock(&bebob->mutex); - amdtp_stream_destroy(&bebob->rx_stream); amdtp_stream_destroy(&bebob->tx_stream); destroy_both_connections(bebob); - - mutex_unlock(&bebob->mutex); } /* diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index 797f0726fc7446..70a111d7f428af 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -236,6 +236,7 @@ static void dice_card_free(struct snd_card *card) { struct snd_dice *dice = card->private_data; + snd_dice_stream_destroy_duplex(dice); snd_dice_transaction_destroy(dice); fw_unit_put(dice->unit); @@ -313,10 +314,6 @@ static void dice_remove(struct fw_unit *unit) { struct snd_dice *dice = dev_get_drvdata(&unit->device); - snd_card_disconnect(dice->card); - - snd_dice_stream_destroy_duplex(dice); - /* No need to wait for releasing card object in this context. */ snd_card_free_when_closed(dice->card); } diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c index 1e33394d8a930e..2682e7e3e5c985 100644 --- a/sound/firewire/fireworks/fireworks.c +++ b/sound/firewire/fireworks/fireworks.c @@ -184,8 +184,12 @@ efw_card_free(struct snd_card *card) { struct snd_efw *efw = card->private_data; + snd_efw_stream_destroy_duplex(efw); + snd_efw_transaction_remove_instance(efw); fw_unit_put(efw->unit); + kfree(efw->resp_buf); + if (efw->card_index >= 0) { mutex_lock(&devices_mutex); clear_bit(efw->card_index, devices_used); @@ -193,7 +197,6 @@ efw_card_free(struct snd_card *card) } mutex_destroy(&efw->mutex); - kfree(efw->resp_buf); } static int @@ -297,11 +300,6 @@ static void efw_remove(struct fw_unit *unit) { struct snd_efw *efw = dev_get_drvdata(&unit->device); - snd_efw_stream_destroy_duplex(efw); - snd_efw_transaction_remove_instance(efw); - - snd_card_disconnect(efw->card); - /* No need to wait for releasing card object in this context. */ snd_card_free_when_closed(efw->card); } diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c index f817b7ae097e28..c55db1bddc80a0 100644 --- a/sound/firewire/fireworks/fireworks_stream.c +++ b/sound/firewire/fireworks/fireworks_stream.c @@ -324,12 +324,8 @@ void snd_efw_stream_update_duplex(struct snd_efw *efw) void snd_efw_stream_destroy_duplex(struct snd_efw *efw) { - mutex_lock(&efw->mutex); - destroy_stream(efw, &efw->rx_stream); destroy_stream(efw, &efw->tx_stream); - - mutex_unlock(&efw->mutex); } void snd_efw_stream_lock_changed(struct snd_efw *efw) diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c index 1607b26404c380..8c6ce019f437c3 100644 --- a/sound/firewire/oxfw/oxfw.c +++ b/sound/firewire/oxfw/oxfw.c @@ -115,6 +115,10 @@ static void oxfw_card_free(struct snd_card *card) struct snd_oxfw *oxfw = card->private_data; unsigned int i; + snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream); + if (oxfw->has_output) + snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream); + fw_unit_put(oxfw->unit); for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { @@ -220,12 +224,6 @@ static void oxfw_remove(struct fw_unit *unit) { struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device); - snd_card_disconnect(oxfw->card); - - snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream); - if (oxfw->has_output) - snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream); - /* No need to wait for releasing card object in this context. */ snd_card_free_when_closed(oxfw->card); } From b983991880eeae927a4ce31bfe76b54e8fd40cf0 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 24 Feb 2015 12:04:57 +0100 Subject: [PATCH 293/788] ALSA: hda: controller code - do not export static functions commit 37ed398839fa3e0d2de77925097db7a370abb096 upstream. It is a bad idea to export static functions. GCC for some platforms shows errors like: error: __ksymtab_azx_get_response causes a section type conflict Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/hda_controller.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 0cfc9c8c4b4e81..c2aa3cd844e58a 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -957,7 +957,6 @@ static int azx_alloc_cmd_io(struct azx *chip) dev_err(chip->card->dev, "cannot allocate CORB/RIRB\n"); return err; } -EXPORT_SYMBOL_GPL(azx_alloc_cmd_io); static void azx_init_cmd_io(struct azx *chip) { @@ -1022,7 +1021,6 @@ static void azx_init_cmd_io(struct azx *chip) azx_writeb(chip, RIRBCTL, AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN); spin_unlock_irq(&chip->reg_lock); } -EXPORT_SYMBOL_GPL(azx_init_cmd_io); static void azx_free_cmd_io(struct azx *chip) { @@ -1032,7 +1030,6 @@ static void azx_free_cmd_io(struct azx *chip) azx_writeb(chip, CORBCTL, 0); spin_unlock_irq(&chip->reg_lock); } -EXPORT_SYMBOL_GPL(azx_free_cmd_io); static unsigned int azx_command_addr(u32 cmd) { @@ -1312,7 +1309,6 @@ static int azx_send_cmd(struct hda_bus *bus, unsigned int val) else return azx_corb_send_cmd(bus, val); } -EXPORT_SYMBOL_GPL(azx_send_cmd); /* get a response */ static unsigned int azx_get_response(struct hda_bus *bus, @@ -1326,7 +1322,6 @@ static unsigned int azx_get_response(struct hda_bus *bus, else return azx_rirb_get_response(bus, addr); } -EXPORT_SYMBOL_GPL(azx_get_response); #ifdef CONFIG_SND_HDA_DSP_LOADER /* From aaec23cdaf872b643da8cc54ed71af37fc27ff39 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 25 Feb 2015 07:53:31 +0100 Subject: [PATCH 294/788] ALSA: hda - Disable runtime PM for Panther Point again commit de5d0ad506cb10ab143e2ffb9def7607e3671f83 upstream. This is essentially a partial revert of the commit [b1920c21102a: 'ALSA: hda - Enable runtime PM on Panther Point']. There was a bug report showing the HD-audio bus hang during runtime PM on HP Spectre XT. Reported-by: Dang Sananikone Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/hda_intel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index d426a0bd6a5f7e..84bed149a41a6f 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2004,7 +2004,7 @@ static const struct pci_device_id azx_ids[] = { .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM }, /* Panther Point */ { PCI_DEVICE(0x8086, 0x1e20), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, + .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM }, /* Lynx Point */ { PCI_DEVICE(0x8086, 0x8c20), .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, From be31e55a3f9d83b1385bdde6cfe13777615bf6ea Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 27 Feb 2015 09:39:32 +0900 Subject: [PATCH 295/788] ALSA: oxfw: fix a condition and return code in start_stream() commit f2b14c0bc510c6a8f67a4f36049deefe5d99a537 upstream. The amdtp_stream_wait_callback() doesn't return minus value and the return code is not for error code. This commit fixes with a propper condition and an error code. Fixes: f3699e2c7745 ('ALSA: oxfw: Change the way to start stream') Reported-by: Dan Carpenter Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/firewire/oxfw/oxfw-stream.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c index 29ccb3637164f8..e6757cd8572422 100644 --- a/sound/firewire/oxfw/oxfw-stream.c +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -171,9 +171,10 @@ static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream, } /* Wait first packet */ - err = amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT); - if (err < 0) + if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) { stop_stream(oxfw, stream); + err = -ETIMEDOUT; + } end: return err; } From 985defee8634240a01da504c81198a4d52aeb028 Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Fri, 6 Mar 2015 14:03:57 +0800 Subject: [PATCH 296/788] ALSA: hda - One more Dell macine needs DELL1_MIC_NO_PRESENCE quirk commit 70658b99490dd86cfdbf4fca117bbe2ef9a80d03 upstream. BugLink: https://bugs.launchpad.net/bugs/1428947 Signed-off-by: Hui Wang Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 0c993f7f9181e2..d3e2fc700c5d48 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5114,6 +5114,13 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x17, 0x40000000}, {0x1d, 0x40700001}, {0x21, 0x02211040}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC255_STANDARD_PINS, + {0x12, 0x90a60170}, + {0x14, 0x90170140}, + {0x17, 0x40000000}, + {0x1d, 0x40700001}, + {0x21, 0x02211050}), SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC280_FIXUP_HP_GPIO4, {0x12, 0x90a60130}, {0x13, 0x40000000}, From 6221586c98cc3d1e3754c29df1206b6fe9f06394 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 17 Feb 2015 16:43:43 +0100 Subject: [PATCH 297/788] locking/rtmutex: Avoid a NULL pointer dereference on deadlock commit 8d1e5a1a1ccf5ae9d8a5a0ee7960202ccb0c5429 upstream. With task_blocks_on_rt_mutex() returning early -EDEADLK we never add the waiter to the waitqueue. Later, we try to remove it via remove_waiter() and go boom in rt_mutex_top_waiter() because rb_entry() gives a NULL pointer. ( Tested on v3.18-RT where rtmutex is used for regular mutex and I tried to get one twice in a row. ) Not sure when this started but I guess 397335f004f4 ("rtmutex: Fix deadlock detector for real") or commit 3d5c9340d194 ("rtmutex: Handle deadlock detection smarter"). Signed-off-by: Sebastian Andrzej Siewior Acked-by: Peter Zijlstra Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/1424187823-19600-1-git-send-email-bigeasy@linutronix.de Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- kernel/locking/rtmutex.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/locking/rtmutex.c b/kernel/locking/rtmutex.c index 7c98873a30777f..41d53e51591407 100644 --- a/kernel/locking/rtmutex.c +++ b/kernel/locking/rtmutex.c @@ -1193,7 +1193,8 @@ rt_mutex_slowlock(struct rt_mutex *lock, int state, set_current_state(TASK_RUNNING); if (unlikely(ret)) { - remove_waiter(lock, &waiter); + if (rt_mutex_has_waiters(lock)) + remove_waiter(lock, &waiter); rt_mutex_handle_deadlock(ret, chwalk, &waiter); } From 003692d8d302302caafddc96d700ca6c0dad257c Mon Sep 17 00:00:00 2001 From: Tony Battersby Date: Wed, 11 Feb 2015 11:32:06 -0500 Subject: [PATCH 298/788] sg: fix read() error reporting commit 3b524a683af8991b4eab4182b947c65f0ce1421b upstream. Fix SCSI generic read() incorrectly returning success after detecting an error. Signed-off-by: Tony Battersby Acked-by: Douglas Gilbert Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/sg.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 763bffe2351745..dbf8e777d850e0 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -546,7 +546,7 @@ static ssize_t sg_new_read(Sg_fd * sfp, char __user *buf, size_t count, Sg_request * srp) { sg_io_hdr_t *hp = &srp->header; - int err = 0; + int err = 0, err2; int len; if (count < SZ_SG_IO_HDR) { @@ -575,8 +575,8 @@ sg_new_read(Sg_fd * sfp, char __user *buf, size_t count, Sg_request * srp) goto err_out; } err_out: - err = sg_finish_rem_req(srp); - return (0 == err) ? count : err; + err2 = sg_finish_rem_req(srp); + return err ? : err2 ? : count; } static ssize_t From 0f3e58c469f29c908b410dc775f4bd08fb523d50 Mon Sep 17 00:00:00 2001 From: Mitko Haralanov Date: Fri, 16 Jan 2015 08:55:27 -0500 Subject: [PATCH 299/788] IB/qib: Do not write EEPROM commit 18c0b82a3e4501511b08d0e8676fb08ac08734a3 upstream. This changeset removes all the code that allows the driver to write to the EEPROM and update the recorded error counters and power on hours. These two stats are unused and writing them exposes a timing risk which could leave the EEPROM in a bad state preventing further normal operation of the HCA. Reviewed-by: Mike Marciniszyn Signed-off-by: Mitko Haralanov Signed-off-by: Mike Marciniszyn Signed-off-by: Roland Dreier Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/hw/qib/qib.h | 9 +- drivers/infiniband/hw/qib/qib_eeprom.c | 181 ------------------------ drivers/infiniband/hw/qib/qib_iba6120.c | 2 - drivers/infiniband/hw/qib/qib_iba7220.c | 2 - drivers/infiniband/hw/qib/qib_iba7322.c | 2 - drivers/infiniband/hw/qib/qib_init.c | 1 - drivers/infiniband/hw/qib/qib_sysfs.c | 24 ---- 7 files changed, 1 insertion(+), 220 deletions(-) diff --git a/drivers/infiniband/hw/qib/qib.h b/drivers/infiniband/hw/qib/qib.h index c00ae093b6f881..b218254ee41bad 100644 --- a/drivers/infiniband/hw/qib/qib.h +++ b/drivers/infiniband/hw/qib/qib.h @@ -1082,12 +1082,6 @@ struct qib_devdata { /* control high-level access to EEPROM */ struct mutex eep_lock; uint64_t traffic_wds; - /* active time is kept in seconds, but logged in hours */ - atomic_t active_time; - /* Below are nominal shadow of EEPROM, new since last EEPROM update */ - uint8_t eep_st_errs[QIB_EEP_LOG_CNT]; - uint8_t eep_st_new_errs[QIB_EEP_LOG_CNT]; - uint16_t eep_hrs; /* * masks for which bits of errs, hwerrs that cause * each of the counters to increment. @@ -1309,8 +1303,7 @@ int qib_twsi_blk_rd(struct qib_devdata *dd, int dev, int addr, void *buffer, int qib_twsi_blk_wr(struct qib_devdata *dd, int dev, int addr, const void *buffer, int len); void qib_get_eeprom_info(struct qib_devdata *); -int qib_update_eeprom_log(struct qib_devdata *dd); -void qib_inc_eeprom_err(struct qib_devdata *dd, u32 eidx, u32 incr); +#define qib_inc_eeprom_err(dd, eidx, incr) void qib_dump_lookup_output_queue(struct qib_devdata *); void qib_force_pio_avail_update(struct qib_devdata *); void qib_clear_symerror_on_linkup(unsigned long opaque); diff --git a/drivers/infiniband/hw/qib/qib_eeprom.c b/drivers/infiniband/hw/qib/qib_eeprom.c index 4d5d71aaa2b4e5..e2280b07df02cb 100644 --- a/drivers/infiniband/hw/qib/qib_eeprom.c +++ b/drivers/infiniband/hw/qib/qib_eeprom.c @@ -267,190 +267,9 @@ void qib_get_eeprom_info(struct qib_devdata *dd) "Board SN %s did not pass functional test: %s\n", dd->serial, ifp->if_comment); - memcpy(&dd->eep_st_errs, &ifp->if_errcntp, QIB_EEP_LOG_CNT); - /* - * Power-on (actually "active") hours are kept as little-endian value - * in EEPROM, but as seconds in a (possibly as small as 24-bit) - * atomic_t while running. - */ - atomic_set(&dd->active_time, 0); - dd->eep_hrs = ifp->if_powerhour[0] | (ifp->if_powerhour[1] << 8); - done: vfree(buf); bail:; } -/** - * qib_update_eeprom_log - copy active-time and error counters to eeprom - * @dd: the qlogic_ib device - * - * Although the time is kept as seconds in the qib_devdata struct, it is - * rounded to hours for re-write, as we have only 16 bits in EEPROM. - * First-cut code reads whole (expected) struct qib_flash, modifies, - * re-writes. Future direction: read/write only what we need, assuming - * that the EEPROM had to have been "good enough" for driver init, and - * if not, we aren't making it worse. - * - */ -int qib_update_eeprom_log(struct qib_devdata *dd) -{ - void *buf; - struct qib_flash *ifp; - int len, hi_water; - uint32_t new_time, new_hrs; - u8 csum; - int ret, idx; - unsigned long flags; - - /* first, check if we actually need to do anything. */ - ret = 0; - for (idx = 0; idx < QIB_EEP_LOG_CNT; ++idx) { - if (dd->eep_st_new_errs[idx]) { - ret = 1; - break; - } - } - new_time = atomic_read(&dd->active_time); - - if (ret == 0 && new_time < 3600) - goto bail; - - /* - * The quick-check above determined that there is something worthy - * of logging, so get current contents and do a more detailed idea. - * read full flash, not just currently used part, since it may have - * been written with a newer definition - */ - len = sizeof(struct qib_flash); - buf = vmalloc(len); - ret = 1; - if (!buf) { - qib_dev_err(dd, - "Couldn't allocate memory to read %u bytes from eeprom for logging\n", - len); - goto bail; - } - - /* Grab semaphore and read current EEPROM. If we get an - * error, let go, but if not, keep it until we finish write. - */ - ret = mutex_lock_interruptible(&dd->eep_lock); - if (ret) { - qib_dev_err(dd, "Unable to acquire EEPROM for logging\n"); - goto free_bail; - } - ret = qib_twsi_blk_rd(dd, dd->twsi_eeprom_dev, 0, buf, len); - if (ret) { - mutex_unlock(&dd->eep_lock); - qib_dev_err(dd, "Unable read EEPROM for logging\n"); - goto free_bail; - } - ifp = (struct qib_flash *)buf; - - csum = flash_csum(ifp, 0); - if (csum != ifp->if_csum) { - mutex_unlock(&dd->eep_lock); - qib_dev_err(dd, "EEPROM cks err (0x%02X, S/B 0x%02X)\n", - csum, ifp->if_csum); - ret = 1; - goto free_bail; - } - hi_water = 0; - spin_lock_irqsave(&dd->eep_st_lock, flags); - for (idx = 0; idx < QIB_EEP_LOG_CNT; ++idx) { - int new_val = dd->eep_st_new_errs[idx]; - if (new_val) { - /* - * If we have seen any errors, add to EEPROM values - * We need to saturate at 0xFF (255) and we also - * would need to adjust the checksum if we were - * trying to minimize EEPROM traffic - * Note that we add to actual current count in EEPROM, - * in case it was altered while we were running. - */ - new_val += ifp->if_errcntp[idx]; - if (new_val > 0xFF) - new_val = 0xFF; - if (ifp->if_errcntp[idx] != new_val) { - ifp->if_errcntp[idx] = new_val; - hi_water = offsetof(struct qib_flash, - if_errcntp) + idx; - } - /* - * update our shadow (used to minimize EEPROM - * traffic), to match what we are about to write. - */ - dd->eep_st_errs[idx] = new_val; - dd->eep_st_new_errs[idx] = 0; - } - } - /* - * Now update active-time. We would like to round to the nearest hour - * but unless atomic_t are sure to be proper signed ints we cannot, - * because we need to account for what we "transfer" to EEPROM and - * if we log an hour at 31 minutes, then we would need to set - * active_time to -29 to accurately count the _next_ hour. - */ - if (new_time >= 3600) { - new_hrs = new_time / 3600; - atomic_sub((new_hrs * 3600), &dd->active_time); - new_hrs += dd->eep_hrs; - if (new_hrs > 0xFFFF) - new_hrs = 0xFFFF; - dd->eep_hrs = new_hrs; - if ((new_hrs & 0xFF) != ifp->if_powerhour[0]) { - ifp->if_powerhour[0] = new_hrs & 0xFF; - hi_water = offsetof(struct qib_flash, if_powerhour); - } - if ((new_hrs >> 8) != ifp->if_powerhour[1]) { - ifp->if_powerhour[1] = new_hrs >> 8; - hi_water = offsetof(struct qib_flash, if_powerhour) + 1; - } - } - /* - * There is a tiny possibility that we could somehow fail to write - * the EEPROM after updating our shadows, but problems from holding - * the spinlock too long are a much bigger issue. - */ - spin_unlock_irqrestore(&dd->eep_st_lock, flags); - if (hi_water) { - /* we made some change to the data, uopdate cksum and write */ - csum = flash_csum(ifp, 1); - ret = eeprom_write_with_enable(dd, 0, buf, hi_water + 1); - } - mutex_unlock(&dd->eep_lock); - if (ret) - qib_dev_err(dd, "Failed updating EEPROM\n"); - -free_bail: - vfree(buf); -bail: - return ret; -} - -/** - * qib_inc_eeprom_err - increment one of the four error counters - * that are logged to EEPROM. - * @dd: the qlogic_ib device - * @eidx: 0..3, the counter to increment - * @incr: how much to add - * - * Each counter is 8-bits, and saturates at 255 (0xFF). They - * are copied to the EEPROM (aka flash) whenever qib_update_eeprom_log() - * is called, but it can only be called in a context that allows sleep. - * This function can be called even at interrupt level. - */ -void qib_inc_eeprom_err(struct qib_devdata *dd, u32 eidx, u32 incr) -{ - uint new_val; - unsigned long flags; - - spin_lock_irqsave(&dd->eep_st_lock, flags); - new_val = dd->eep_st_new_errs[eidx] + incr; - if (new_val > 255) - new_val = 255; - dd->eep_st_new_errs[eidx] = new_val; - spin_unlock_irqrestore(&dd->eep_st_lock, flags); -} diff --git a/drivers/infiniband/hw/qib/qib_iba6120.c b/drivers/infiniband/hw/qib/qib_iba6120.c index d68266ac7619b8..f7f49a6c34b00f 100644 --- a/drivers/infiniband/hw/qib/qib_iba6120.c +++ b/drivers/infiniband/hw/qib/qib_iba6120.c @@ -2681,8 +2681,6 @@ static void qib_get_6120_faststats(unsigned long opaque) spin_lock_irqsave(&dd->eep_st_lock, flags); traffic_wds -= dd->traffic_wds; dd->traffic_wds += traffic_wds; - if (traffic_wds >= QIB_TRAFFIC_ACTIVE_THRESHOLD) - atomic_add(5, &dd->active_time); /* S/B #define */ spin_unlock_irqrestore(&dd->eep_st_lock, flags); qib_chk_6120_errormask(dd); diff --git a/drivers/infiniband/hw/qib/qib_iba7220.c b/drivers/infiniband/hw/qib/qib_iba7220.c index 7dec89fdc1248d..f5fa106e19926e 100644 --- a/drivers/infiniband/hw/qib/qib_iba7220.c +++ b/drivers/infiniband/hw/qib/qib_iba7220.c @@ -3297,8 +3297,6 @@ static void qib_get_7220_faststats(unsigned long opaque) spin_lock_irqsave(&dd->eep_st_lock, flags); traffic_wds -= dd->traffic_wds; dd->traffic_wds += traffic_wds; - if (traffic_wds >= QIB_TRAFFIC_ACTIVE_THRESHOLD) - atomic_add(5, &dd->active_time); /* S/B #define */ spin_unlock_irqrestore(&dd->eep_st_lock, flags); done: mod_timer(&dd->stats_timer, jiffies + HZ * ACTIVITY_TIMER); diff --git a/drivers/infiniband/hw/qib/qib_iba7322.c b/drivers/infiniband/hw/qib/qib_iba7322.c index a7eb32517a04bc..23ca2aca1ad66b 100644 --- a/drivers/infiniband/hw/qib/qib_iba7322.c +++ b/drivers/infiniband/hw/qib/qib_iba7322.c @@ -5178,8 +5178,6 @@ static void qib_get_7322_faststats(unsigned long opaque) spin_lock_irqsave(&ppd->dd->eep_st_lock, flags); traffic_wds -= ppd->dd->traffic_wds; ppd->dd->traffic_wds += traffic_wds; - if (traffic_wds >= QIB_TRAFFIC_ACTIVE_THRESHOLD) - atomic_add(ACTIVITY_TIMER, &ppd->dd->active_time); spin_unlock_irqrestore(&ppd->dd->eep_st_lock, flags); if (ppd->cpspec->qdr_dfe_on && (ppd->link_speed_active & QIB_IB_QDR) && diff --git a/drivers/infiniband/hw/qib/qib_init.c b/drivers/infiniband/hw/qib/qib_init.c index 729da39c49ed3f..738269b46d83d2 100644 --- a/drivers/infiniband/hw/qib/qib_init.c +++ b/drivers/infiniband/hw/qib/qib_init.c @@ -931,7 +931,6 @@ static void qib_shutdown_device(struct qib_devdata *dd) qib_free_pportdata(ppd); } - qib_update_eeprom_log(dd); } /** diff --git a/drivers/infiniband/hw/qib/qib_sysfs.c b/drivers/infiniband/hw/qib/qib_sysfs.c index 3c8e4e3caca624..b9ccbda7817d17 100644 --- a/drivers/infiniband/hw/qib/qib_sysfs.c +++ b/drivers/infiniband/hw/qib/qib_sysfs.c @@ -611,28 +611,6 @@ static ssize_t store_chip_reset(struct device *device, return ret < 0 ? ret : count; } -static ssize_t show_logged_errs(struct device *device, - struct device_attribute *attr, char *buf) -{ - struct qib_ibdev *dev = - container_of(device, struct qib_ibdev, ibdev.dev); - struct qib_devdata *dd = dd_from_dev(dev); - int idx, count; - - /* force consistency with actual EEPROM */ - if (qib_update_eeprom_log(dd) != 0) - return -ENXIO; - - count = 0; - for (idx = 0; idx < QIB_EEP_LOG_CNT; ++idx) { - count += scnprintf(buf + count, PAGE_SIZE - count, "%d%c", - dd->eep_st_errs[idx], - idx == (QIB_EEP_LOG_CNT - 1) ? '\n' : ' '); - } - - return count; -} - /* * Dump tempsense regs. in decimal, to ease shell-scripts. */ @@ -679,7 +657,6 @@ static DEVICE_ATTR(nctxts, S_IRUGO, show_nctxts, NULL); static DEVICE_ATTR(nfreectxts, S_IRUGO, show_nfreectxts, NULL); static DEVICE_ATTR(serial, S_IRUGO, show_serial, NULL); static DEVICE_ATTR(boardversion, S_IRUGO, show_boardversion, NULL); -static DEVICE_ATTR(logged_errors, S_IRUGO, show_logged_errs, NULL); static DEVICE_ATTR(tempsense, S_IRUGO, show_tempsense, NULL); static DEVICE_ATTR(localbus_info, S_IRUGO, show_localbus_info, NULL); static DEVICE_ATTR(chip_reset, S_IWUSR, NULL, store_chip_reset); @@ -693,7 +670,6 @@ static struct device_attribute *qib_attributes[] = { &dev_attr_nfreectxts, &dev_attr_serial, &dev_attr_boardversion, - &dev_attr_logged_errors, &dev_attr_tempsense, &dev_attr_localbus_info, &dev_attr_chip_reset, From 90bbda5070bfa913b4d7b50e4b31659fe4994776 Mon Sep 17 00:00:00 2001 From: Sagi Grimberg Date: Sun, 18 Jan 2015 16:51:06 +0200 Subject: [PATCH 300/788] IB/iser: Fix memory regions possible leak commit 6606e6a2ff2710b473838b291dc533cd8fc1471f upstream. When teardown process starts during live IO, we need to keep the memory regions pool (frmr/fmr) until all in-flight tasks are properly released, since each task may return a memory region to the pool. In order to do this, we pass a destroy flag to iser_free_ib_conn_res to indicate we can destroy the device and the memory regions pool. iser_conn_release will pass it as true and also DEVICE_REMOVAL event (we need to let the device to properly remove). Also, Since we conditionally call iser_free_rx_descriptors, remove the extra check on iser_conn->rx_descs. Fixes: 5426b1711fd0 ("IB/iser: Collapse cleanup and disconnect handlers") Reported-by: Or Gerlitz Signed-off-by: Sagi Grimberg Signed-off-by: Roland Dreier Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/ulp/iser/iser_initiator.c | 4 ---- drivers/infiniband/ulp/iser/iser_verbs.c | 25 +++++++++++--------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c index 3821633f1065b1..de382af10cc8d8 100644 --- a/drivers/infiniband/ulp/iser/iser_initiator.c +++ b/drivers/infiniband/ulp/iser/iser_initiator.c @@ -320,9 +320,6 @@ void iser_free_rx_descriptors(struct iser_conn *iser_conn) struct ib_conn *ib_conn = &iser_conn->ib_conn; struct iser_device *device = ib_conn->device; - if (!iser_conn->rx_descs) - goto free_login_buf; - if (device->iser_free_rdma_reg_res) device->iser_free_rdma_reg_res(ib_conn); @@ -334,7 +331,6 @@ void iser_free_rx_descriptors(struct iser_conn *iser_conn) /* make sure we never redo any unmapping */ iser_conn->rx_descs = NULL; -free_login_buf: iser_free_login_buf(iser_conn); } diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c index 695a2704bd4380..f3e21abc20a630 100644 --- a/drivers/infiniband/ulp/iser/iser_verbs.c +++ b/drivers/infiniband/ulp/iser/iser_verbs.c @@ -600,16 +600,16 @@ void iser_release_work(struct work_struct *work) /** * iser_free_ib_conn_res - release IB related resources * @iser_conn: iser connection struct - * @destroy_device: indicator if we need to try to release - * the iser device (only iscsi shutdown and DEVICE_REMOVAL - * will use this. + * @destroy: indicator if we need to try to release the + * iser device and memory regoins pool (only iscsi + * shutdown and DEVICE_REMOVAL will use this). * * This routine is called with the iser state mutex held * so the cm_id removal is out of here. It is Safe to * be invoked multiple times. */ static void iser_free_ib_conn_res(struct iser_conn *iser_conn, - bool destroy_device) + bool destroy) { struct ib_conn *ib_conn = &iser_conn->ib_conn; struct iser_device *device = ib_conn->device; @@ -617,17 +617,20 @@ static void iser_free_ib_conn_res(struct iser_conn *iser_conn, iser_info("freeing conn %p cma_id %p qp %p\n", iser_conn, ib_conn->cma_id, ib_conn->qp); - iser_free_rx_descriptors(iser_conn); - if (ib_conn->qp != NULL) { ib_conn->comp->active_qps--; rdma_destroy_qp(ib_conn->cma_id); ib_conn->qp = NULL; } - if (destroy_device && device != NULL) { - iser_device_try_release(device); - ib_conn->device = NULL; + if (destroy) { + if (iser_conn->rx_descs) + iser_free_rx_descriptors(iser_conn); + + if (device != NULL) { + iser_device_try_release(device); + ib_conn->device = NULL; + } } } @@ -840,7 +843,7 @@ static void iser_disconnected_handler(struct rdma_cm_id *cma_id) } static void iser_cleanup_handler(struct rdma_cm_id *cma_id, - bool destroy_device) + bool destroy) { struct iser_conn *iser_conn = (struct iser_conn *)cma_id->context; @@ -850,7 +853,7 @@ static void iser_cleanup_handler(struct rdma_cm_id *cma_id, * and flush errors. */ iser_disconnected_handler(cma_id); - iser_free_ib_conn_res(iser_conn, destroy_device); + iser_free_ib_conn_res(iser_conn, destroy); complete(&iser_conn->ib_completion); }; From 2a86bea695e4c6a6678e48596f8785b66789dae7 Mon Sep 17 00:00:00 2001 From: Roi Dayan Date: Sun, 28 Dec 2014 14:26:11 +0200 Subject: [PATCH 301/788] IB/iser: Use correct dma direction when unmapping SGs commit c6c95ef4cec680f7a10aa425a9970744b35b6489 upstream. We always unmap SGs with the same direction instead of unmapping with the direction the mapping was done, fix that. Fixes: 9a8b08fad2ef ("IB/iser: Generalize iser_unmap_task_data and [...]") Signed-off-by: Roi Dayan Signed-off-by: Or Gerlitz Signed-off-by: Roland Dreier Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/ulp/iser/iscsi_iser.h | 4 +++- drivers/infiniband/ulp/iser/iser_initiator.c | 12 ++++++++---- drivers/infiniband/ulp/iser/iser_memory.c | 9 ++++++--- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.h b/drivers/infiniband/ulp/iser/iscsi_iser.h index 5ce26817e7e1d9..b47aea1094b2d9 100644 --- a/drivers/infiniband/ulp/iser/iscsi_iser.h +++ b/drivers/infiniband/ulp/iser/iscsi_iser.h @@ -654,7 +654,9 @@ int iser_dma_map_task_data(struct iscsi_iser_task *iser_task, enum dma_data_direction dma_dir); void iser_dma_unmap_task_data(struct iscsi_iser_task *iser_task, - struct iser_data_buf *data); + struct iser_data_buf *data, + enum dma_data_direction dir); + int iser_initialize_task_headers(struct iscsi_task *task, struct iser_tx_desc *tx_desc); int iser_alloc_rx_descriptors(struct iser_conn *iser_conn, diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c index de382af10cc8d8..20e859a6f1a63b 100644 --- a/drivers/infiniband/ulp/iser/iser_initiator.c +++ b/drivers/infiniband/ulp/iser/iser_initiator.c @@ -710,19 +710,23 @@ void iser_task_rdma_finalize(struct iscsi_iser_task *iser_task) device->iser_unreg_rdma_mem(iser_task, ISER_DIR_IN); if (is_rdma_data_aligned) iser_dma_unmap_task_data(iser_task, - &iser_task->data[ISER_DIR_IN]); + &iser_task->data[ISER_DIR_IN], + DMA_FROM_DEVICE); if (prot_count && is_rdma_prot_aligned) iser_dma_unmap_task_data(iser_task, - &iser_task->prot[ISER_DIR_IN]); + &iser_task->prot[ISER_DIR_IN], + DMA_FROM_DEVICE); } if (iser_task->dir[ISER_DIR_OUT]) { device->iser_unreg_rdma_mem(iser_task, ISER_DIR_OUT); if (is_rdma_data_aligned) iser_dma_unmap_task_data(iser_task, - &iser_task->data[ISER_DIR_OUT]); + &iser_task->data[ISER_DIR_OUT], + DMA_TO_DEVICE); if (prot_count && is_rdma_prot_aligned) iser_dma_unmap_task_data(iser_task, - &iser_task->prot[ISER_DIR_OUT]); + &iser_task->prot[ISER_DIR_OUT], + DMA_TO_DEVICE); } } diff --git a/drivers/infiniband/ulp/iser/iser_memory.c b/drivers/infiniband/ulp/iser/iser_memory.c index abce9339333f0a..341040bf09849d 100644 --- a/drivers/infiniband/ulp/iser/iser_memory.c +++ b/drivers/infiniband/ulp/iser/iser_memory.c @@ -332,12 +332,13 @@ int iser_dma_map_task_data(struct iscsi_iser_task *iser_task, } void iser_dma_unmap_task_data(struct iscsi_iser_task *iser_task, - struct iser_data_buf *data) + struct iser_data_buf *data, + enum dma_data_direction dir) { struct ib_device *dev; dev = iser_task->iser_conn->ib_conn.device->ib_device; - ib_dma_unmap_sg(dev, data->buf, data->size, DMA_FROM_DEVICE); + ib_dma_unmap_sg(dev, data->buf, data->size, dir); } static int fall_to_bounce_buf(struct iscsi_iser_task *iser_task, @@ -357,7 +358,9 @@ static int fall_to_bounce_buf(struct iscsi_iser_task *iser_task, iser_data_buf_dump(mem, ibdev); /* unmap the command data before accessing it */ - iser_dma_unmap_task_data(iser_task, mem); + iser_dma_unmap_task_data(iser_task, mem, + (cmd_dir == ISER_DIR_OUT) ? + DMA_TO_DEVICE : DMA_FROM_DEVICE); /* allocate copy buf, if we are writing, copy the */ /* unaligned scatterlist, dma map the copy */ From b3f855626a6ccc309472f4adb2217b019af39f2b Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 12 Jan 2015 11:56:58 +0300 Subject: [PATCH 302/788] IB/mlx5: Fix error code in get_port_caps() commit f614fc15ae39ceb531586e3969f2b99fd23182a0 upstream. The current code returns success when kmalloc() fails. It should return an error code, -ENOMEM. Fixes: e126ba97dba9 ("mlx5: Add driver for Mellanox Connect-IB adapters") Signed-off-by: Dan Carpenter Signed-off-by: Roland Dreier Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/hw/mlx5/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 03bf81211a5401..b1eda4a602a80a 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -997,7 +997,7 @@ static int get_port_caps(struct mlx5_ib_dev *dev) struct ib_device_attr *dprops = NULL; struct ib_port_attr *pprops = NULL; struct mlx5_general_caps *gen; - int err = 0; + int err = -ENOMEM; int port; gen = &dev->mdev->caps.gen; From 419672eea8f4f0fd1a7692e066389f1094ea152d Mon Sep 17 00:00:00 2001 From: Majd Dibbiny Date: Thu, 29 Jan 2015 10:41:41 +0200 Subject: [PATCH 303/788] IB/mlx4: Fix memory leak in __mlx4_ib_modify_qp commit bede98e781747623ae170667694a71ef19c6ba7f upstream. In case handle_eth_ud_smac_index fails, we need to free the allocated resources. Fixes: 2f5bb473681b ("mlx4: Add ref counting to port MAC table for RoCE") Signed-off-by: Majd Dibbiny Signed-off-by: Or Gerlitz Signed-off-by: Roland Dreier Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/hw/mlx4/qp.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c index cf000b7ad64f9b..c880329b4d6469 100644 --- a/drivers/infiniband/hw/mlx4/qp.c +++ b/drivers/infiniband/hw/mlx4/qp.c @@ -1674,8 +1674,10 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp, qp->mlx4_ib_qp_type == MLX4_IB_QPT_PROXY_GSI || qp->mlx4_ib_qp_type == MLX4_IB_QPT_TUN_GSI) { err = handle_eth_ud_smac_index(dev, qp, (u8 *)attr->smac, context); - if (err) - return -EINVAL; + if (err) { + err = -EINVAL; + goto out; + } if (qp->mlx4_ib_qp_type == MLX4_IB_QPT_PROXY_GSI) dev->qp1_proxy[qp->port - 1] = qp; } From d524fcea1f1bef297cae09a9d8bde4f60ed0c61e Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Wed, 17 Dec 2014 16:17:34 +0200 Subject: [PATCH 304/788] IB/mlx4: Fix wrong usage of IPv4 protocol for multicast attach/detach commit e9a7faf11af94957e5107b40af46c2e329541510 upstream. The MLX4_PROT_IB_IPV4 protocol should only be used with RoCEv2 and such. Removing this wrong usage allows to run multicast applications over RoCE. Fixes: d487ee77740c ("IB/mlx4: Use IBoE (RoCE) IP based GIDs in the port GID table") Reported-by: Carol Soto Signed-off-by: Or Gerlitz Signed-off-by: Roland Dreier Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/hw/mlx4/main.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index 9117b7a2d5f8be..0b280b1c98dfb8 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -1222,8 +1222,7 @@ static int mlx4_ib_mcg_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) struct mlx4_ib_qp *mqp = to_mqp(ibqp); u64 reg_id; struct mlx4_ib_steering *ib_steering = NULL; - enum mlx4_protocol prot = (gid->raw[1] == 0x0e) ? - MLX4_PROT_IB_IPV4 : MLX4_PROT_IB_IPV6; + enum mlx4_protocol prot = MLX4_PROT_IB_IPV6; if (mdev->dev->caps.steering_mode == MLX4_STEERING_MODE_DEVICE_MANAGED) { @@ -1236,8 +1235,10 @@ static int mlx4_ib_mcg_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) !!(mqp->flags & MLX4_IB_QP_BLOCK_MULTICAST_LOOPBACK), prot, ®_id); - if (err) + if (err) { + pr_err("multicast attach op failed, err %d\n", err); goto err_malloc; + } err = add_gid_entry(ibqp, gid); if (err) @@ -1285,8 +1286,7 @@ static int mlx4_ib_mcg_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) struct net_device *ndev; struct mlx4_ib_gid_entry *ge; u64 reg_id = 0; - enum mlx4_protocol prot = (gid->raw[1] == 0x0e) ? - MLX4_PROT_IB_IPV4 : MLX4_PROT_IB_IPV6; + enum mlx4_protocol prot = MLX4_PROT_IB_IPV6; if (mdev->dev->caps.steering_mode == MLX4_STEERING_MODE_DEVICE_MANAGED) { From db2b084cd0e1fd7f98b0b82965b2d6581bc79e02 Mon Sep 17 00:00:00 2001 From: Moshe Lazer Date: Thu, 5 Feb 2015 13:53:52 +0200 Subject: [PATCH 305/788] IB/core: Fix deadlock on uverbs modify_qp error flow commit 0fb8bcf022f19a375d7c4bd79ac513da8ae6d78b upstream. The deadlock occurs in __uverbs_modify_qp: we take a lock (idr_read_qp) and in case of failure in ib_resolve_eth_l2_attrs we don't release it (put_qp_read). Fix that. Fixes: ed4c54e5b4ba ("IB/core: Resolve Ethernet L2 addresses when modifying QP") Signed-off-by: Moshe Lazer Signed-off-by: Or Gerlitz Signed-off-by: Roland Dreier Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/core/uverbs_cmd.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c index b7943ff16ed3f2..6c52e72f8d2cf9 100644 --- a/drivers/infiniband/core/uverbs_cmd.c +++ b/drivers/infiniband/core/uverbs_cmd.c @@ -2091,20 +2091,21 @@ ssize_t ib_uverbs_modify_qp(struct ib_uverbs_file *file, if (qp->real_qp == qp) { ret = ib_resolve_eth_l2_attrs(qp, attr, &cmd.attr_mask); if (ret) - goto out; + goto release_qp; ret = qp->device->modify_qp(qp, attr, modify_qp_mask(qp->qp_type, cmd.attr_mask), &udata); } else { ret = ib_modify_qp(qp, attr, modify_qp_mask(qp->qp_type, cmd.attr_mask)); } - put_qp_read(qp); - if (ret) - goto out; + goto release_qp; ret = in_len; +release_qp: + put_qp_read(qp); + out: kfree(attr); From 26f9e525c4993205c7119859f2046ec726e6c25d Mon Sep 17 00:00:00 2001 From: Haggai Eran Date: Tue, 6 Jan 2015 13:56:02 +0200 Subject: [PATCH 306/788] IB/core: Properly handle registration of on-demand paging MRs after dereg commit 4fc701ead77ede96df3e8b3de13fdf2b1326ee5b upstream. When the last on-demand paging MR is released the notifier count is left non-zero so that concurrent page faults will have to abort. If a new MR is then registered, the counter is reset. However, the decision is made to put the new MR in the list waiting for the notifier count to reach zero, before the counter is reset. An invalidation or another MR registration can release the MR to handle page faults, but without such an event the MR can wait forever. The patch fixes this issue by adding a check whether the MR is the first on-demand paging MR when deciding whether it is ready to handle page faults. If it is the first MR, we know that there are no mmu notifiers running in parallel to the registration. Fixes: 882214e2b128 ("IB/core: Implement support for MMU notifiers regarding on demand paging regions") Signed-off-by: Haggai Eran Signed-off-by: Shachar Raindel Signed-off-by: Roland Dreier Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/core/umem_odp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/core/umem_odp.c b/drivers/infiniband/core/umem_odp.c index 6095872549e79f..8b8cc6fa0ab0c1 100644 --- a/drivers/infiniband/core/umem_odp.c +++ b/drivers/infiniband/core/umem_odp.c @@ -294,7 +294,8 @@ int ib_umem_odp_get(struct ib_ucontext *context, struct ib_umem *umem) if (likely(ib_umem_start(umem) != ib_umem_end(umem))) rbt_ib_umem_insert(&umem->odp_data->interval_tree, &context->umem_tree); - if (likely(!atomic_read(&context->notifier_count))) + if (likely(!atomic_read(&context->notifier_count)) || + context->odp_mrs_count == 1) umem->odp_data->mn_counters_active = true; else list_add(&umem->odp_data->no_private_counters, From 00da0c72472a5512bd8fccf52973b6c4f545793b Mon Sep 17 00:00:00 2001 From: Ilya Nelkenbaum Date: Thu, 5 Feb 2015 13:53:48 +0200 Subject: [PATCH 307/788] IB/core: When marshaling ucma path from user-space, clear unused fields commit c2be9dc0e0fa59cc43c2c7084fc42b430809a0fe upstream. When marshaling a user path to the kernel struct ib_sa_path, we need to zero smac and dmac and set the vlan id to the "no vlan" value. This is to ensure that Ethernet attributes are not used with InfiniBand QPs. Fixes: dd5f03beb4f7 ("IB/core: Ethernet L2 attributes in verbs/cm structures") Signed-off-by: Ilya Nelkenbaum Signed-off-by: Or Gerlitz Signed-off-by: Roland Dreier Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/core/ucma.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c index 56a4b7ca7ee38b..45d67e9228d75b 100644 --- a/drivers/infiniband/core/ucma.c +++ b/drivers/infiniband/core/ucma.c @@ -1124,6 +1124,9 @@ static int ucma_set_ib_path(struct ucma_context *ctx, if (!optlen) return -EINVAL; + memset(&sa_path, 0, sizeof(sa_path)); + sa_path.vlan_id = 0xffff; + ib_sa_unpack_path(path_data->path_rec, &sa_path); ret = rdma_set_ib_paths(ctx->cm_id, &sa_path, 1); if (ret) From f2ad09ddf093623f966a62102d1590b42d2b6fa1 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Fri, 27 Feb 2015 15:51:56 -0800 Subject: [PATCH 308/788] nilfs2: fix potential memory overrun on inode commit 957ed60b53b519064a54988c4e31e0087e47d091 upstream. Each inode of nilfs2 stores a root node of a b-tree, and it turned out to have a memory overrun issue: Each b-tree node of nilfs2 stores a set of key-value pairs and the number of them (in "bn_nchildren" member of nilfs_btree_node struct), as well as a few other "bn_*" members. Since the value of "bn_nchildren" is used for operations on the key-values within the b-tree node, it can cause memory access overrun if a large number is incorrectly set to "bn_nchildren". For instance, nilfs_btree_node_lookup() function determines the range of binary search with it, and too large "bn_nchildren" leads nilfs_btree_node_get_key() in that function to overrun. As for intermediate b-tree nodes, this is prevented by a sanity check performed when each node is read from a drive, however, no sanity check has been done for root nodes stored in inodes. This patch fixes the issue by adding missing sanity check against b-tree root nodes so that it's called when on-memory inodes are read from ifile, inode metadata file. Signed-off-by: Ryusuke Konishi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/nilfs2/btree.c | 47 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/fs/nilfs2/btree.c b/fs/nilfs2/btree.c index b2e3ff34762070..ecdbae19a766d9 100644 --- a/fs/nilfs2/btree.c +++ b/fs/nilfs2/btree.c @@ -31,6 +31,8 @@ #include "alloc.h" #include "dat.h" +static void __nilfs_btree_init(struct nilfs_bmap *bmap); + static struct nilfs_btree_path *nilfs_btree_alloc_path(void) { struct nilfs_btree_path *path; @@ -368,6 +370,34 @@ static int nilfs_btree_node_broken(const struct nilfs_btree_node *node, return ret; } +/** + * nilfs_btree_root_broken - verify consistency of btree root node + * @node: btree root node to be examined + * @ino: inode number + * + * Return Value: If node is broken, 1 is returned. Otherwise, 0 is returned. + */ +static int nilfs_btree_root_broken(const struct nilfs_btree_node *node, + unsigned long ino) +{ + int level, flags, nchildren; + int ret = 0; + + level = nilfs_btree_node_get_level(node); + flags = nilfs_btree_node_get_flags(node); + nchildren = nilfs_btree_node_get_nchildren(node); + + if (unlikely(level < NILFS_BTREE_LEVEL_NODE_MIN || + level > NILFS_BTREE_LEVEL_MAX || + nchildren < 0 || + nchildren > NILFS_BTREE_ROOT_NCHILDREN_MAX)) { + pr_crit("NILFS: bad btree root (inode number=%lu): level = %d, flags = 0x%x, nchildren = %d\n", + ino, level, flags, nchildren); + ret = 1; + } + return ret; +} + int nilfs_btree_broken_node_block(struct buffer_head *bh) { int ret; @@ -1713,7 +1743,7 @@ nilfs_btree_commit_convert_and_insert(struct nilfs_bmap *btree, /* convert and insert */ dat = NILFS_BMAP_USE_VBN(btree) ? nilfs_bmap_get_dat(btree) : NULL; - nilfs_btree_init(btree); + __nilfs_btree_init(btree); if (nreq != NULL) { nilfs_bmap_commit_alloc_ptr(btree, dreq, dat); nilfs_bmap_commit_alloc_ptr(btree, nreq, dat); @@ -2294,12 +2324,23 @@ static const struct nilfs_bmap_operations nilfs_btree_ops_gc = { .bop_gather_data = NULL, }; -int nilfs_btree_init(struct nilfs_bmap *bmap) +static void __nilfs_btree_init(struct nilfs_bmap *bmap) { bmap->b_ops = &nilfs_btree_ops; bmap->b_nchildren_per_block = NILFS_BTREE_NODE_NCHILDREN_MAX(nilfs_btree_node_size(bmap)); - return 0; +} + +int nilfs_btree_init(struct nilfs_bmap *bmap) +{ + int ret = 0; + + __nilfs_btree_init(bmap); + + if (nilfs_btree_root_broken(nilfs_btree_get_root(bmap), + bmap->b_inode->i_ino)) + ret = -EIO; + return ret; } void nilfs_btree_init_gc(struct nilfs_bmap *bmap) From 189a021fffea59620d912989d14e1eff15710e3a Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Mon, 9 Feb 2015 13:38:21 +0100 Subject: [PATCH 309/788] wd719x: add missing .module to wd719x_template commit 2ecf8e0ae28cb22d434e628c351c6193fd75fafa upstream. wd719x_template is missing the .module field, causing module refcount not to work, allowing to rmmod the driver while in use (mounted filesystem), causing an oops. Set .module to THIS_MODULE to fix the problem. Signed-off-by: Ondrej Zary Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/wd719x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/scsi/wd719x.c b/drivers/scsi/wd719x.c index 7702664d7ed325..289ad016d92504 100644 --- a/drivers/scsi/wd719x.c +++ b/drivers/scsi/wd719x.c @@ -870,6 +870,7 @@ static int wd719x_board_found(struct Scsi_Host *sh) } static struct scsi_host_template wd719x_template = { + .module = THIS_MODULE, .name = "Western Digital 719x", .queuecommand = wd719x_queuecommand, .eh_abort_handler = wd719x_abort, From 3b62b65857443af31f1b92b7bbcf0419ea4aa857 Mon Sep 17 00:00:00 2001 From: Minh Duc Tran Date: Mon, 9 Feb 2015 18:54:09 +0000 Subject: [PATCH 310/788] fixed invalid assignment of 64bit mask to host dma_boundary for scatter gather segment boundary limit. commit f76a610a8b4b6280eaedf48f3af9d5d74e418b66 upstream. In reference to bug https://bugzilla.redhat.com/show_bug.cgi?id=1097141 Assert is seen with AMD cpu whenever calling pci_alloc_consistent. [ 29.406183] ------------[ cut here ]------------ [ 29.410505] kernel BUG at lib/iommu-helper.c:13! Signed-off-by: Minh Tran Fixes: 6733b39a1301b0b020bbcbf3295852e93e624cb1 Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/be2iscsi/be_main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index f3193406776cb9..9cc047bc763b86 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -586,7 +586,6 @@ static struct beiscsi_hba *beiscsi_hba_alloc(struct pci_dev *pcidev) "beiscsi_hba_alloc - iscsi_host_alloc failed\n"); return NULL; } - shost->dma_boundary = pcidev->dma_mask; shost->max_id = BE2_MAX_SESSIONS; shost->max_channel = 0; shost->max_cmd_len = BEISCSI_MAX_CMD_LEN; From dad96ab777646836b86ec41701f22c3148c31ee2 Mon Sep 17 00:00:00 2001 From: Soren Brinkmann Date: Tue, 27 Jan 2015 11:05:27 -0800 Subject: [PATCH 311/788] clk: zynq: Force CPU_2X clock to be ungated commit 3dccfecdb867fe35b305a4e493ef5652b7d9d4cb upstream. The CPU_2X clock does not have a classical in-kernel user, but is, amongst other things, required for OCM and debug access. Make sure this clock is not mistakenly disabled during boot up by enabling it in the platform's clock driver. Fixes: 0ee52b157b8e 'clk: zynq: Add clock controller driver' Signed-off-by: Soren Brinkmann Signed-off-by: Michael Turquette Signed-off-by: Greg Kroah-Hartman --- drivers/clk/zynq/clkc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/zynq/clkc.c b/drivers/clk/zynq/clkc.c index 9037bebd69f79c..f870aad57711f6 100644 --- a/drivers/clk/zynq/clkc.c +++ b/drivers/clk/zynq/clkc.c @@ -303,6 +303,7 @@ static void __init zynq_clk_setup(struct device_node *np) clks[cpu_2x] = clk_register_gate(NULL, clk_output_name[cpu_2x], "cpu_2x_div", CLK_IGNORE_UNUSED, SLCR_ARM_CLK_CTRL, 26, 0, &armclk_lock); + clk_prepare_enable(clks[cpu_2x]); clk = clk_register_fixed_factor(NULL, "cpu_1x_div", "cpu_div", 0, 1, 4 + 2 * tmp); From 1cd24fc0a79caee54b79408c2e0ea816c8eacaf2 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 19 Jan 2015 09:57:13 +0000 Subject: [PATCH 312/788] clk: Fix debugfs clk removal before inited commit 52bba9809a954d72bc77773bd560b9724b495eb7 upstream. Some of the clks can be registered & unregistered before the clk related debugfs entries are initialized at late_initcall. In the unregister path checking for only dentry before clk_debug_init() would lead dangling pointers in the debug clk list, because the list is already populated in register path and the clk pointer freed in unregister path. The side effect of not removing it from the list is either a null pointer dereference or if lucky to boot the system, the number of clk entries in debugfs disappear. We could add more checks like if (inited && !clk->dentry) but just removing the check for dentry made more sense as debugfs_remove_recursive() seems to be safe with null pointers. This will ensure that the unregistering clk would be removed from the debug list in all the code paths. Without this patch kernel would crash with log: Unable to handle kernel NULL pointer dereference at virtual address 00000000 pgd = c0204000 [00000000] *pgd=00000000 Internal error: Oops: 5 [#1] SMP ARM Modules linked in: CPU: 1 PID: 1 Comm: swapper/0 Tainted: G B 3.19.0-rc3-00007-g412f9ba-dirty #840 Hardware name: Qualcomm (Flattened Device Tree) task: ed948000 ti: ed944000 task.ti: ed944000 PC is at strlen+0xc/0x40 LR is at __create_file+0x64/0x1dc pc : [] lr : [] psr: 60000013 sp : ed945e40 ip : ed945e50 fp : ed945e4c r10: 00000000 r9 : c1006094 r8 : 00000000 r7 : 000041ed r6 : 00000000 r5 : ed4af998 r4 : c11b5e28 r3 : 00000000 r2 : ed945e38 r1 : a0000013 r0 : 00000000 Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel Control: 10c5787d Table: 8020406a DAC: 00000015 Process swapper/0 (pid: 1, stack limit = 0xed944248) Stack: (0xed945e40 to 0xed946000) 5e40: ed945e7c ed945e50 c049f1c4 c04ee604 c0fc2fa4 00000000 ecb748c0 c11c2b80 5e60: c0beec04 0000011c c0fc2fa4 00000000 ed945e94 ed945e80 c049f3e0 c049f16c 5e80: 00000000 00000000 ed945eac ed945e98 c08cbc50 c049f3c0 ecb748c0 c11c2b80 5ea0: ed945ed4 ed945eb0 c0fc3080 c08cbc30 c0beec04 c107e1d8 ecdf0600 c107e1d8 5ec0: c107e1d8 ecdf0600 ed945f54 ed945ed8 c0208ed4 c0fc2fb0 c026a784 c04ee628 5ee0: ed945f0c ed945ef0 c0f5d600 c04ee604 c0f5d5ec ef7fcc7d c0b40ecc 0000011c 5f00: ed945f54 ed945f10 c026a994 c0f5d5f8 c04ecc00 00000007 ef7fcc95 00000007 5f20: c0e90744 c0dd0884 ed945f54 c106cde0 00000007 c117f8c0 0000011c c0f5d5ec 5f40: c1006094 c100609c ed945f94 ed945f58 c0f5de34 c0208e50 00000007 00000007 5f60: c0f5d5ec be9b5ae0 00000000 c117f8c0 c0af1680 00000000 00000000 00000000 5f80: 00000000 00000000 ed945fac ed945f98 c0af169c c0f5dd2c ed944000 00000000 5fa0: 00000000 ed945fb0 c020f298 c0af168c 00000000 00000000 00000000 00000000 5fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 5fe0: 00000000 00000000 00000000 00000000 00000013 00000000 ebcc6d33 bfffca73 [] (strlen) from [] (__create_file+0x64/0x1dc) [] (__create_file) from [] (debugfs_create_dir+0x2c/0x34) [] (debugfs_create_dir) from [] (clk_debug_create_one+0x2c/0x16c) [] (clk_debug_create_one) from [] (clk_debug_init+0xdc/0x144) [] (clk_debug_init) from [] (do_one_initcall+0x90/0x1e0) [] (do_one_initcall) from [] (kernel_init_freeable+0x114/0x1e0) [] (kernel_init_freeable) from [] (kernel_init+0x1c/0xfc) [] (kernel_init) from [] (ret_from_fork+0x14/0x3c) Code: c0b40ecc e1a0c00d e92dd800 e24cb004 (e5d02000) ---[ end trace b940e45b5e25c1e7 ]--- Fixes: 6314b6796e3c "clk: Don't hold prepare_lock across debugfs creation" Signed-off-by: Srinivas Kandagatla Reviewed-by: Stephen Boyd Signed-off-by: Michael Turquette Signed-off-by: Greg Kroah-Hartman --- drivers/clk/clk.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index d48ac71c6c8b17..2f14f57507fbc7 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -343,13 +343,9 @@ static int clk_debug_register(struct clk *clk) static void clk_debug_unregister(struct clk *clk) { mutex_lock(&clk_debug_lock); - if (!clk->dentry) - goto out; - hlist_del_init(&clk->debug_node); debugfs_remove_recursive(clk->dentry); clk->dentry = NULL; -out: mutex_unlock(&clk_debug_lock); } From a3290574a76c29b219fb3982f41860ba134c36fb Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 24 Jan 2015 12:56:32 +0100 Subject: [PATCH 313/788] sunxi: clk: Set sun6i-pll1 n_start = 1 commit 76820fcf7aa5a418b69cb7bed31b62d1feb1d6ad upstream. For all pll-s on sun6i n == 0 means use a multiplier of 1, rather then 0 as it means on sun4i / sun5i / sun7i. n_start = 1 is already correctly set for sun6i pll6, but was missing for pll1, this commit fixes this. Cc: Chen-Yu Tsai Signed-off-by: Hans de Goede Signed-off-by: Maxime Ripard Signed-off-by: Greg Kroah-Hartman --- drivers/clk/sunxi/clk-sunxi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 570202582dcfef..80f5693be6ee85 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -413,6 +413,7 @@ static struct clk_factors_config sun6i_a31_pll1_config = { .kwidth = 2, .mshift = 0, .mwidth = 2, + .n_start = 1, }; static struct clk_factors_config sun8i_a23_pll1_config = { From 6ceed5c2a0a42ae9171338da039ef1b19efd790f Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Tue, 27 Jan 2015 18:16:51 +0000 Subject: [PATCH 314/788] staging: comedi: comedi_compat32.c: fix COMEDI_CMD copy back commit 42b8ce6f55facfa101462e694d33fc6bca471138 upstream. `do_cmd_ioctl()` in "comedi_fops.c" handles the `COMEDI_CMD` ioctl. This returns `-EAGAIN` if it has copied a modified `struct comedi_cmd` back to user-space. (This occurs when the low-level Comedi driver's `do_cmdtest()` handler returns non-zero to indicate a problem with the contents of the `struct comedi_cmd`, or when the `struct comedi_cmd` has the `CMDF_BOGUS` flag set.) `compat_cmd()` in "comedi_compat32.c" handles the 32-bit compatible version of the `COMEDI_CMD` ioctl. Currently, it never copies a 32-bit compatible version of `struct comedi_cmd` back to user-space, which is at odds with the way the regular `COMEDI_CMD` ioctl is handled. To fix it, change `compat_cmd()` to copy a 32-bit compatible version of the `struct comedi_cmd` back to user-space when the main ioctl handler returns `-EAGAIN`. Signed-off-by: Ian Abbott Reviewed-by: H Hartley Sweeten Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/comedi_compat32.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/staging/comedi/comedi_compat32.c b/drivers/staging/comedi/comedi_compat32.c index 5a4c74f703b3bd..03a2d07844192a 100644 --- a/drivers/staging/comedi/comedi_compat32.c +++ b/drivers/staging/comedi/comedi_compat32.c @@ -262,7 +262,7 @@ static int compat_cmd(struct file *file, unsigned long arg) { struct comedi_cmd __user *cmd; struct comedi32_cmd_struct __user *cmd32; - int rc; + int rc, err; cmd32 = compat_ptr(arg); cmd = compat_alloc_user_space(sizeof(*cmd)); @@ -271,7 +271,15 @@ static int compat_cmd(struct file *file, unsigned long arg) if (rc) return rc; - return translated_ioctl(file, COMEDI_CMD, (unsigned long)cmd); + rc = translated_ioctl(file, COMEDI_CMD, (unsigned long)cmd); + if (rc == -EAGAIN) { + /* Special case: copy cmd back to user. */ + err = put_compat_cmd(cmd32, cmd); + if (err) + rc = err; + } + + return rc; } /* Handle 32-bit COMEDI_CMDTEST ioctl. */ From 5eb2654c6829c2e5dc8f8d4d641b93148bcca300 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 12 Feb 2015 10:09:20 -0500 Subject: [PATCH 315/788] dm mirror: do not degrade the mirror on discard error commit f2ed51ac64611d717d1917820a01930174c2f236 upstream. It may be possible that a device claims discard support but it rejects discards with -EOPNOTSUPP. It happens when using loopback on ext2/ext3 filesystem driven by the ext4 driver. It may also happen if the underlying devices are moved from one disk on another. If discard error happens, we reject the bio with -EOPNOTSUPP, but we do not degrade the array. This patch fixes failed test shell/lvconvert-repair-transient.sh in the lvm2 testsuite if the testsuite is extracted on an ext2 or ext3 filesystem and it is being driven by the ext4 driver. Signed-off-by: Mikulas Patocka Signed-off-by: Mike Snitzer Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-raid1.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c index 7dfdb5c746d6f3..089d62751f7ff2 100644 --- a/drivers/md/dm-raid1.c +++ b/drivers/md/dm-raid1.c @@ -604,6 +604,15 @@ static void write_callback(unsigned long error, void *context) return; } + /* + * If the bio is discard, return an error, but do not + * degrade the array. + */ + if (bio->bi_rw & REQ_DISCARD) { + bio_endio(bio, -EOPNOTSUPP); + return; + } + for (i = 0; i < ms->nr_mirrors; i++) if (test_bit(i, &error)) fail_mirror(ms->mirror + i, DM_RAID1_WRITE_ERROR); From 144ab376d2c0342dc0caa050b078a1d9ebb04a9d Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Fri, 13 Feb 2015 11:05:37 -0800 Subject: [PATCH 316/788] dm io: reject unsupported DISCARD requests with EOPNOTSUPP commit 37527b869207ad4c208b1e13967d69b8bba1fbf9 upstream. I created a dm-raid1 device backed by a device that supports DISCARD and another device that does NOT support DISCARD with the following dm configuration: # echo '0 2048 mirror core 1 512 2 /dev/sda 0 /dev/sdb 0' | dmsetup create moo # lsblk -D NAME DISC-ALN DISC-GRAN DISC-MAX DISC-ZERO sda 0 4K 1G 0 `-moo (dm-0) 0 4K 1G 0 sdb 0 0B 0B 0 `-moo (dm-0) 0 4K 1G 0 Notice that the mirror device /dev/mapper/moo advertises DISCARD support even though one of the mirror halves doesn't. If I issue a DISCARD request (via fstrim, mount -o discard, or ioctl BLKDISCARD) through the mirror, kmirrord gets stuck in an infinite loop in do_region() when it tries to issue a DISCARD request to sdb. The problem is that when we call do_region() against sdb, num_sectors is set to zero because q->limits.max_discard_sectors is zero. Therefore, "remaining" never decreases and the loop never terminates. To fix this: before entering the loop, check for the combination of REQ_DISCARD and no discard and return -EOPNOTSUPP to avoid hanging up the mirror device. This bug was found by the unfortunate coincidence of pvmove and a discard operation in the RHEL 6.5 kernel; upstream is also affected. Signed-off-by: Darrick J. Wong Acked-by: "Martin K. Petersen" Signed-off-by: Mike Snitzer Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-io.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/md/dm-io.c b/drivers/md/dm-io.c index c09359db3a9073..37de0173b6d232 100644 --- a/drivers/md/dm-io.c +++ b/drivers/md/dm-io.c @@ -290,6 +290,12 @@ static void do_region(int rw, unsigned region, struct dm_io_region *where, unsigned short logical_block_size = queue_logical_block_size(q); sector_t num_sectors; + /* Reject unsupported discard requests */ + if ((rw & REQ_DISCARD) && !blk_queue_discard(q)) { + dec_count(io, region, -EOPNOTSUPP); + return; + } + /* * where->count may be zero if rw holds a flush and we need to * send a zero-sized flush. From e6bafcd14630f6fd84a326f1d51ce1466dfd60df Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Tue, 17 Feb 2015 14:30:53 -0500 Subject: [PATCH 317/788] dm: fix a race condition in dm_get_md commit 2bec1f4a8832e74ebbe859f176d8a9cb20dd97f4 upstream. The function dm_get_md finds a device mapper device with a given dev_t, increases the reference count and returns the pointer. dm_get_md calls dm_find_md, dm_find_md takes _minor_lock, finds the device, tests that the device doesn't have DMF_DELETING or DMF_FREEING flag, drops _minor_lock and returns pointer to the device. dm_get_md then calls dm_get. dm_get calls BUG if the device has the DMF_FREEING flag, otherwise it increments the reference count. There is a possible race condition - after dm_find_md exits and before dm_get is called, there are no locks held, so the device may disappear or DMF_FREEING flag may be set, which results in BUG. To fix this bug, we need to call dm_get while we hold _minor_lock. This patch renames dm_find_md to dm_get_md and changes it so that it calls dm_get while holding the lock. Signed-off-by: Mikulas Patocka Signed-off-by: Mike Snitzer Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm.c | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 2caf5b374649af..64b10e006f9c63 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -2462,7 +2462,7 @@ int dm_setup_md_queue(struct mapped_device *md) return 0; } -static struct mapped_device *dm_find_md(dev_t dev) +struct mapped_device *dm_get_md(dev_t dev) { struct mapped_device *md; unsigned minor = MINOR(dev); @@ -2473,12 +2473,15 @@ static struct mapped_device *dm_find_md(dev_t dev) spin_lock(&_minor_lock); md = idr_find(&_minor_idr, minor); - if (md && (md == MINOR_ALLOCED || - (MINOR(disk_devt(dm_disk(md))) != minor) || - dm_deleting_md(md) || - test_bit(DMF_FREEING, &md->flags))) { - md = NULL; - goto out; + if (md) { + if ((md == MINOR_ALLOCED || + (MINOR(disk_devt(dm_disk(md))) != minor) || + dm_deleting_md(md) || + test_bit(DMF_FREEING, &md->flags))) { + md = NULL; + goto out; + } + dm_get(md); } out: @@ -2486,16 +2489,6 @@ static struct mapped_device *dm_find_md(dev_t dev) return md; } - -struct mapped_device *dm_get_md(dev_t dev) -{ - struct mapped_device *md = dm_find_md(dev); - - if (md) - dm_get(md); - - return md; -} EXPORT_SYMBOL_GPL(dm_get_md); void *dm_get_mdptr(struct mapped_device *md) From 0a694d185c0856800f7045b9621a50b704c7302d Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Tue, 17 Feb 2015 14:34:00 -0500 Subject: [PATCH 318/788] dm snapshot: fix a possible invalid memory access on unload commit 22aa66a3ee5b61e0f4a0bfeabcaa567861109ec3 upstream. When the snapshot target is unloaded, snapshot_dtr() waits until pending_exceptions_count drops to zero. Then, it destroys the snapshot. Therefore, the function that decrements pending_exceptions_count should not touch the snapshot structure after the decrement. pending_complete() calls free_pending_exception(), which decrements pending_exceptions_count, and then it performs up_write(&s->lock) and it calls retry_origin_bios() which dereferences s->origin. These two memory accesses to the fields of the snapshot may touch the dm_snapshot struture after it is freed. This patch moves the call to free_pending_exception() to the end of pending_complete(), so that the snapshot will not be destroyed while pending_complete() is in progress. Signed-off-by: Mikulas Patocka Signed-off-by: Mike Snitzer Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-snap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index 864b03f477276f..8b204ae216ab62 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c @@ -1432,8 +1432,6 @@ static void pending_complete(struct dm_snap_pending_exception *pe, int success) full_bio->bi_private = pe->full_bio_private; atomic_inc(&full_bio->bi_remaining); } - free_pending_exception(pe); - increment_pending_exceptions_done_count(); up_write(&s->lock); @@ -1450,6 +1448,8 @@ static void pending_complete(struct dm_snap_pending_exception *pe, int success) } retry_origin_bios(s, origin_bios); + + free_pending_exception(pe); } static void commit_callback(void *context, int success) From 96b012a4a065da89401e7cffd7558269b18ca0be Mon Sep 17 00:00:00 2001 From: Ivan Khoronzhuk Date: Wed, 18 Feb 2015 13:33:19 +0200 Subject: [PATCH 319/788] firmware: dmi_scan: Fix dmi_len type commit 6d9ff473317245e3e5cd9922b4520411c2296388 upstream. According to SMBIOSv3 specification the length of DMI table can be up to 32bits wide. So use appropriate type to avoid overflow. It's obvious that dmi_num theoretically can be more than u16 also, so it's can be changed to u32 or at least it's better to use int instead of u16, but on that moment I cannot imagine dmi structure count more than 65535 and it can require changing type of vars that work with it. So I didn't correct it. Acked-by: Ard Biesheuvel Signed-off-by: Ivan Khoronzhuk Signed-off-by: Matt Fleming Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/dmi_scan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index c5f7b4e9eb6c6e..c03d0c17845816 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -78,7 +78,7 @@ static const char * __init dmi_string(const struct dmi_header *dm, u8 s) * We have to be cautious here. We have seen BIOSes with DMI pointers * pointing to completely the wrong place for example */ -static void dmi_table(u8 *buf, int len, int num, +static void dmi_table(u8 *buf, u32 len, int num, void (*decode)(const struct dmi_header *, void *), void *private_data) { @@ -114,7 +114,7 @@ static void dmi_table(u8 *buf, int len, int num, } static phys_addr_t dmi_base; -static u16 dmi_len; +static u32 dmi_len; static u16 dmi_num; static int __init dmi_walk_early(void (*decode)(const struct dmi_header *, From 632151d9f8d9179a975e81870baae4481de7e915 Mon Sep 17 00:00:00 2001 From: Ivan Khoronzhuk Date: Wed, 18 Feb 2015 15:51:41 +0200 Subject: [PATCH 320/788] firmware: dmi_scan: Fix dmi scan to handle "End of Table" structure commit ce204e9a4bd82e9e6e7479bca8057e45aaac5c42 upstream. The dmi-sysfs should create "End of Table" entry, that is type 127. But after adding initial SMBIOS v3 support fc43026278b2 ("dmi: add support for SMBIOS 3.0 64-bit entry point") the 127-0 entry is not handled any more, as result it's not created in dmi sysfs for instance. This is important because the size of whole DMI table must correspond to sum of all DMI entry sizes. So move the end-of-table check after it's handled by dmi_table. Reviewed-by: Ard Biesheuvel Signed-off-by: Ivan Khoronzhuk Signed-off-by: Matt Fleming Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/dmi_scan.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index c03d0c17845816..69fac068669fde 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -92,12 +92,6 @@ static void dmi_table(u8 *buf, u32 len, int num, while ((i < num) && (data - buf + sizeof(struct dmi_header)) <= len) { const struct dmi_header *dm = (const struct dmi_header *)data; - /* - * 7.45 End-of-Table (Type 127) [SMBIOS reference spec v3.0.0] - */ - if (dm->type == DMI_ENTRY_END_OF_TABLE) - break; - /* * We want to know the total length (formatted area and * strings) before decoding to make sure we won't run off the @@ -108,6 +102,13 @@ static void dmi_table(u8 *buf, u32 len, int num, data++; if (data - buf < len - 1) decode(dm, private_data); + + /* + * 7.45 End-of-Table (Type 127) [SMBIOS reference spec v3.0.0] + */ + if (dm->type == DMI_ENTRY_END_OF_TABLE) + break; + data += 2; i++; } From 330a8414f07972938cad16721d85e1f696c8d6b8 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Mon, 19 Jan 2015 14:47:27 +0000 Subject: [PATCH 321/788] staging: comedi: cb_pcidas64: fix incorrect AI range code handling commit be8e89087ec2d2c8a1ad1e3db64bf4efdfc3c298 upstream. The hardware range code values and list of valid ranges for the AI subdevice is incorrect for several supported boards. The hardware range code values for all boards except PCI-DAS4020/12 is determined by calling `ai_range_bits_6xxx()` based on the maximum voltage of the range and whether it is bipolar or unipolar, however it only returns the correct hardware range code for the PCI-DAS60xx boards. For PCI-DAS6402/16 (and /12) it returns the wrong code for the unipolar ranges. For PCI-DAS64/Mx/16 it returns the wrong code for all the ranges and the comedi range table is incorrect. Change `ai_range_bits_6xxx()` to use a look-up table pointed to by new member `ai_range_codes` of `struct pcidas64_board` to map the comedi range table indices to the hardware range codes. Use a new comedi range table for the PCI-DAS64/Mx/16 boards (and the commented out variants). Signed-off-by: Ian Abbott Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/drivers/cb_pcidas64.c | 122 ++++++++++++------- 1 file changed, 75 insertions(+), 47 deletions(-) diff --git a/drivers/staging/comedi/drivers/cb_pcidas64.c b/drivers/staging/comedi/drivers/cb_pcidas64.c index eddb7ace43df76..569310a9135be3 100644 --- a/drivers/staging/comedi/drivers/cb_pcidas64.c +++ b/drivers/staging/comedi/drivers/cb_pcidas64.c @@ -439,6 +439,29 @@ static const struct comedi_lrange ai_ranges_64xx = { } }; +static const uint8_t ai_range_code_64xx[8] = { + 0x0, 0x1, 0x2, 0x3, /* bipolar 10, 5, 2,5, 1.25 */ + 0x8, 0x9, 0xa, 0xb /* unipolar 10, 5, 2.5, 1.25 */ +}; + +/* analog input ranges for 64-Mx boards */ +static const struct comedi_lrange ai_ranges_64_mx = { + 7, { + BIP_RANGE(5), + BIP_RANGE(2.5), + BIP_RANGE(1.25), + BIP_RANGE(0.625), + UNI_RANGE(5), + UNI_RANGE(2.5), + UNI_RANGE(1.25) + } +}; + +static const uint8_t ai_range_code_64_mx[7] = { + 0x0, 0x1, 0x2, 0x3, /* bipolar 5, 2.5, 1.25, 0.625 */ + 0x9, 0xa, 0xb /* unipolar 5, 2.5, 1.25 */ +}; + /* analog input ranges for 60xx boards */ static const struct comedi_lrange ai_ranges_60xx = { 4, { @@ -449,6 +472,10 @@ static const struct comedi_lrange ai_ranges_60xx = { } }; +static const uint8_t ai_range_code_60xx[4] = { + 0x0, 0x1, 0x4, 0x7 /* bipolar 10, 5, 0.5, 0.05 */ +}; + /* analog input ranges for 6030, etc boards */ static const struct comedi_lrange ai_ranges_6030 = { 14, { @@ -469,6 +496,11 @@ static const struct comedi_lrange ai_ranges_6030 = { } }; +static const uint8_t ai_range_code_6030[14] = { + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, /* bip 10, 5, 2, 1, 0.5, 0.2, 0.1 */ + 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf /* uni 10, 5, 2, 1, 0.5, 0.2, 0.1 */ +}; + /* analog input ranges for 6052, etc boards */ static const struct comedi_lrange ai_ranges_6052 = { 15, { @@ -490,6 +522,11 @@ static const struct comedi_lrange ai_ranges_6052 = { } }; +static const uint8_t ai_range_code_6052[15] = { + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, /* bipolar 10 ... 0.05 */ + 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf /* unipolar 10 ... 0.1 */ +}; + /* analog input ranges for 4020 board */ static const struct comedi_lrange ai_ranges_4020 = { 2, { @@ -593,6 +630,7 @@ struct pcidas64_board { int ai_bits; /* analog input resolution */ int ai_speed; /* fastest conversion period in ns */ const struct comedi_lrange *ai_range_table; + const uint8_t *ai_range_code; int ao_nchan; /* number of analog out channels */ int ao_bits; /* analog output resolution */ int ao_scan_speed; /* analog output scan speed */ @@ -651,6 +689,7 @@ static const struct pcidas64_board pcidas64_boards[] = { .ao_scan_speed = 10000, .layout = LAYOUT_64XX, .ai_range_table = &ai_ranges_64xx, + .ai_range_code = ai_range_code_64xx, .ao_range_table = &ao_ranges_64xx, .ao_range_code = ao_range_code_64xx, .ai_fifo = &ai_fifo_64xx, @@ -666,6 +705,7 @@ static const struct pcidas64_board pcidas64_boards[] = { .ao_scan_speed = 10000, .layout = LAYOUT_64XX, .ai_range_table = &ai_ranges_64xx, + .ai_range_code = ai_range_code_64xx, .ao_range_table = &ao_ranges_64xx, .ao_range_code = ao_range_code_64xx, .ai_fifo = &ai_fifo_64xx, @@ -680,7 +720,8 @@ static const struct pcidas64_board pcidas64_boards[] = { .ao_bits = 16, .ao_scan_speed = 10000, .layout = LAYOUT_64XX, - .ai_range_table = &ai_ranges_64xx, + .ai_range_table = &ai_ranges_64_mx, + .ai_range_code = ai_range_code_64_mx, .ao_range_table = &ao_ranges_64xx, .ao_range_code = ao_range_code_64xx, .ai_fifo = &ai_fifo_64xx, @@ -695,7 +736,8 @@ static const struct pcidas64_board pcidas64_boards[] = { .ao_bits = 16, .ao_scan_speed = 10000, .layout = LAYOUT_64XX, - .ai_range_table = &ai_ranges_64xx, + .ai_range_table = &ai_ranges_64_mx, + .ai_range_code = ai_range_code_64_mx, .ao_range_table = &ao_ranges_64xx, .ao_range_code = ao_range_code_64xx, .ai_fifo = &ai_fifo_64xx, @@ -710,7 +752,8 @@ static const struct pcidas64_board pcidas64_boards[] = { .ao_bits = 16, .ao_scan_speed = 10000, .layout = LAYOUT_64XX, - .ai_range_table = &ai_ranges_64xx, + .ai_range_table = &ai_ranges_64_mx, + .ai_range_code = ai_range_code_64_mx, .ao_range_table = &ao_ranges_64xx, .ao_range_code = ao_range_code_64xx, .ai_fifo = &ai_fifo_64xx, @@ -725,6 +768,7 @@ static const struct pcidas64_board pcidas64_boards[] = { .ao_bits = 16, .layout = LAYOUT_60XX, .ai_range_table = &ai_ranges_60xx, + .ai_range_code = ai_range_code_60xx, .ao_range_table = &range_bipolar10, .ao_range_code = ao_range_code_60xx, .ai_fifo = &ai_fifo_60xx, @@ -740,6 +784,7 @@ static const struct pcidas64_board pcidas64_boards[] = { .ao_scan_speed = 100000, .layout = LAYOUT_60XX, .ai_range_table = &ai_ranges_60xx, + .ai_range_code = ai_range_code_60xx, .ao_range_table = &range_bipolar10, .ao_range_code = ao_range_code_60xx, .ai_fifo = &ai_fifo_60xx, @@ -754,6 +799,7 @@ static const struct pcidas64_board pcidas64_boards[] = { .ao_scan_speed = 100000, .layout = LAYOUT_60XX, .ai_range_table = &ai_ranges_60xx, + .ai_range_code = ai_range_code_60xx, .ao_range_table = &range_bipolar10, .ao_range_code = ao_range_code_60xx, .ai_fifo = &ai_fifo_60xx, @@ -769,6 +815,7 @@ static const struct pcidas64_board pcidas64_boards[] = { .ao_scan_speed = 100000, .layout = LAYOUT_60XX, .ai_range_table = &ai_ranges_60xx, + .ai_range_code = ai_range_code_60xx, .ao_range_table = &range_bipolar10, .ao_range_code = ao_range_code_60xx, .ai_fifo = &ai_fifo_60xx, @@ -784,6 +831,7 @@ static const struct pcidas64_board pcidas64_boards[] = { .ao_scan_speed = 10000, .layout = LAYOUT_60XX, .ai_range_table = &ai_ranges_6030, + .ai_range_code = ai_range_code_6030, .ao_range_table = &ao_ranges_6030, .ao_range_code = ao_range_code_6030, .ai_fifo = &ai_fifo_60xx, @@ -799,6 +847,7 @@ static const struct pcidas64_board pcidas64_boards[] = { .ao_scan_speed = 10000, .layout = LAYOUT_60XX, .ai_range_table = &ai_ranges_6030, + .ai_range_code = ai_range_code_6030, .ao_range_table = &ao_ranges_6030, .ao_range_code = ao_range_code_6030, .ai_fifo = &ai_fifo_60xx, @@ -812,6 +861,7 @@ static const struct pcidas64_board pcidas64_boards[] = { .ao_nchan = 0, .layout = LAYOUT_60XX, .ai_range_table = &ai_ranges_6030, + .ai_range_code = ai_range_code_6030, .ai_fifo = &ai_fifo_60xx, .has_8255 = 0, }, @@ -823,6 +873,7 @@ static const struct pcidas64_board pcidas64_boards[] = { .ao_nchan = 0, .layout = LAYOUT_60XX, .ai_range_table = &ai_ranges_6030, + .ai_range_code = ai_range_code_6030, .ai_fifo = &ai_fifo_60xx, .has_8255 = 0, }, @@ -835,6 +886,7 @@ static const struct pcidas64_board pcidas64_boards[] = { .ao_scan_speed = 0, .layout = LAYOUT_60XX, .ai_range_table = &ai_ranges_60xx, + .ai_range_code = ai_range_code_60xx, .ai_fifo = &ai_fifo_60xx, .has_8255 = 0, }, @@ -848,6 +900,7 @@ static const struct pcidas64_board pcidas64_boards[] = { .ao_scan_speed = 100000, .layout = LAYOUT_60XX, .ai_range_table = &ai_ranges_60xx, + .ai_range_code = ai_range_code_60xx, .ao_range_table = &range_bipolar10, .ao_range_code = ao_range_code_60xx, .ai_fifo = &ai_fifo_60xx, @@ -863,6 +916,7 @@ static const struct pcidas64_board pcidas64_boards[] = { .ao_scan_speed = 100000, .layout = LAYOUT_60XX, .ai_range_table = &ai_ranges_60xx, + .ai_range_code = ai_range_code_60xx, .ao_range_table = &range_bipolar10, .ao_range_code = ao_range_code_60xx, .ai_fifo = &ai_fifo_60xx, @@ -878,6 +932,7 @@ static const struct pcidas64_board pcidas64_boards[] = { .ao_scan_speed = 1000, .layout = LAYOUT_60XX, .ai_range_table = &ai_ranges_6052, + .ai_range_code = ai_range_code_6052, .ao_range_table = &ao_ranges_6030, .ao_range_code = ao_range_code_6030, .ai_fifo = &ai_fifo_60xx, @@ -893,6 +948,7 @@ static const struct pcidas64_board pcidas64_boards[] = { .ao_scan_speed = 3333, .layout = LAYOUT_60XX, .ai_range_table = &ai_ranges_6052, + .ai_range_code = ai_range_code_6052, .ao_range_table = &ao_ranges_6030, .ao_range_code = ao_range_code_6030, .ai_fifo = &ai_fifo_60xx, @@ -908,6 +964,7 @@ static const struct pcidas64_board pcidas64_boards[] = { .ao_scan_speed = 1000, .layout = LAYOUT_60XX, .ai_range_table = &ai_ranges_6052, + .ai_range_code = ai_range_code_6052, .ao_range_table = &ao_ranges_6030, .ao_range_code = ao_range_code_6030, .ai_fifo = &ai_fifo_60xx, @@ -923,6 +980,7 @@ static const struct pcidas64_board pcidas64_boards[] = { .ao_scan_speed = 1000, .layout = LAYOUT_60XX, .ai_range_table = &ai_ranges_6052, + .ai_range_code = ai_range_code_6052, .ao_range_table = &ao_ranges_6030, .ao_range_code = ao_range_code_6030, .ai_fifo = &ai_fifo_60xx, @@ -957,6 +1015,7 @@ static const struct pcidas64_board pcidas64_boards[] = { .ao_scan_speed = 10000, .layout = LAYOUT_64XX, .ai_range_table = &ai_ranges_64xx, + .ai_range_code = ai_range_code_64xx, .ai_fifo = ai_fifo_64xx, .has_8255 = 1, }, @@ -968,7 +1027,8 @@ static const struct pcidas64_board pcidas64_boards[] = { .ao_nchan = 0, .ao_scan_speed = 10000, .layout = LAYOUT_64XX, - .ai_range_table = &ai_ranges_64xx, + .ai_range_table = &ai_ranges_64_mx, + .ai_range_code = ai_range_code_64_mx, .ai_fifo = ai_fifo_64xx, .has_8255 = 1, }, @@ -980,7 +1040,8 @@ static const struct pcidas64_board pcidas64_boards[] = { .ao_nchan = 0, .ao_scan_speed = 10000, .layout = LAYOUT_64XX, - .ai_range_table = &ai_ranges_64xx, + .ai_range_table = &ai_ranges_64_mx, + .ai_range_code = ai_range_code_64_mx, .ai_fifo = ai_fifo_64xx, .has_8255 = 1, }, @@ -992,7 +1053,8 @@ static const struct pcidas64_board pcidas64_boards[] = { .ao_nchan = 0, .ao_scan_speed = 10000, .layout = LAYOUT_64XX, - .ai_range_table = &ai_ranges_64xx, + .ai_range_table = &ai_ranges_64_mx, + .ai_range_code = ai_range_code_64_mx, .ai_fifo = ai_fifo_64xx, .has_8255 = 1, }, @@ -1004,7 +1066,8 @@ static const struct pcidas64_board pcidas64_boards[] = { .ao_nchan = 2, .ao_scan_speed = 10000, .layout = LAYOUT_64XX, - .ai_range_table = &ai_ranges_64xx, + .ai_range_table = &ai_ranges_64_mx, + .ai_range_code = ai_range_code_64_mx, .ai_fifo = ai_fifo_64xx, .has_8255 = 1, }, @@ -1016,7 +1079,8 @@ static const struct pcidas64_board pcidas64_boards[] = { .ao_nchan = 2, .ao_scan_speed = 10000, .layout = LAYOUT_64XX, - .ai_range_table = &ai_ranges_64xx, + .ai_range_table = &ai_ranges_64_mx, + .ai_range_code = ai_range_code_64_mx, .ai_fifo = ai_fifo_64xx, .has_8255 = 1, }, @@ -1028,7 +1092,8 @@ static const struct pcidas64_board pcidas64_boards[] = { .ao_nchan = 2, .ao_scan_speed = 10000, .layout = LAYOUT_64XX, - .ai_range_table = &ai_ranges_64xx, + .ai_range_table = &ai_ranges_64_mx, + .ai_range_code = ai_range_code_64_mx, .ai_fifo = ai_fifo_64xx, .has_8255 = 1, }, @@ -1115,45 +1180,8 @@ static unsigned int ai_range_bits_6xxx(const struct comedi_device *dev, unsigned int range_index) { const struct pcidas64_board *thisboard = dev->board_ptr; - const struct comedi_krange *range = - &thisboard->ai_range_table->range[range_index]; - unsigned int bits = 0; - switch (range->max) { - case 10000000: - bits = 0x000; - break; - case 5000000: - bits = 0x100; - break; - case 2000000: - case 2500000: - bits = 0x200; - break; - case 1000000: - case 1250000: - bits = 0x300; - break; - case 500000: - bits = 0x400; - break; - case 200000: - case 250000: - bits = 0x500; - break; - case 100000: - bits = 0x600; - break; - case 50000: - bits = 0x700; - break; - default: - dev_err(dev->class_dev, "bug! in %s\n", __func__); - break; - } - if (range->min == 0) - bits += 0x900; - return bits; + return thisboard->ai_range_code[range_index] << 8; } static unsigned int hw_revision(const struct comedi_device *dev, From e146d4ebec69c68556d41cdbd65bdfb3da90f20a Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Mon, 29 Dec 2014 15:21:26 +0100 Subject: [PATCH 322/788] HID: input: fix confusion on conflicting mappings commit 6ce901eb61aa30ba8565c62049ee80c90728ef14 upstream. On an PC-101/103/104 keyboard (American layout) the 'Enter' key and its neighbours look like this: +---+ +---+ +-------+ | 1 | | 2 | | 5 | +---+ +---+ +-------+ +---+ +-----------+ | 3 | | 4 | +---+ +-----------+ On a PC-102/105 keyboard (European layout) it looks like this: +---+ +---+ +-------+ | 1 | | 2 | | | +---+ +---+ +-+ 4 | +---+ +---+ | | | 3 | | 5 | | | +---+ +---+ +-----+ (Note that the number of keys is the same, but key '5' is moved down and the shape of key '4' is changed. Keys '1' to '3' are exactly the same.) The keys 1-4 report the same scan-code in HID in both layouts, even though the keysym they produce is usually different depending on the XKB-keymap used by user-space. However, key '5' (US 'backslash'/'pipe') reports 0x31 for the upper layout and 0x32 for the lower layout, as defined by the HID spec. This is highly confusing as the linux-input API uses a single keycode for both. So far, this was never a problem as there never has been a keyboard with both of those keys present at the same time. It would have to look something like this: +---+ +---+ +-------+ | 1 | | 2 | | x31 | +---+ +---+ +-------+ +---+ +---+ +-----+ | 3 | |x32| | 4 | +---+ +---+ +-----+ HID can represent such a keyboard, but the linux-input API cannot. Furthermore, any user-space mapping would be confused by this and, luckily, no-one ever produced such hardware. Now, the HID input layer fixed this mess by mapping both 0x31 and 0x32 to the same keycode (KEY_BACKSLASH==0x2b). As only one of both physical keys is present on a hardware, this works just fine. Lets introduce hardware-vendors into this: ------------------------------------------ Unfortunately, it seems way to expensive to produce a different device for American and European layouts. Therefore, hardware-vendors put both keys, (0x31 and 0x32) on the same keyboard, but only one of them is hooked up to the physical button, the other one is 'dead'. This means, they can use the same hardware, with a different button-layout and automatically produce the correct HID events for American *and* European layouts. This is unproblematic for normal keyboards, as the 'dead' key will never report any KEY-DOWN events. But RollOver keyboards send the whole matrix on each key-event, allowing n-key roll-over mode. This means, we get a 0x31 and 0x32 event on each key-press. One of them will always be 0, the other reports the real state. As we map both to the same keycode, we will get spurious key-events, even though the real key-state never changed. The easiest way would be to blacklist 'dead' keys and never handle those. We could simply read the 'country' tag of USB devices and blacklist either key according to the layout. But... hardware vendors... want the same device for all countries and thus many of them set 'country' to 0 for all devices. Meh.. So we have to deal with this properly. As we cannot know which of the keys is 'dead', we either need a heuristic and track those keys, or we simply make use of our value-tracking for HID fields. We simply ignore HID events for absolute data if the data didn't change. As HID tracks events on the HID level, we haven't done the keycode translation, yet. Therefore, the 'dead' key is tracked independently of the real key, therefore, any events on it will be ignored. This patch simply discards any HID events for absolute data if it didn't change compared to the last report. We need to ignore relative and buffered-byte reports for obvious reasons. But those cannot be affected by this bug, so we're fine. Preferably, we'd do this filtering on the HID-core level. But this might break a lot of custom drivers, if they do not follow the HID specs. Therefore, we do this late in hid-input just before we inject it into the input layer (which does the exact same filtering, but on the keycode level). If this turns out to break some devices, we might have to limit filtering to EV_KEY events. But lets try to do the Right Thing first, and properly filter any absolute data that didn't change. This patch is tagged for 'stable' as it fixes a lot of n-key RollOver hardware. We might wanna wait with backporting for a while, before we know it doesn't break anything else, though. Reported-by: Adam Goode Reported-by: Fredrik Hallenberg Tested-by: Fredrik Hallenberg Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-input.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 9505605b6e22a7..cb9c6900df11a2 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1104,6 +1104,22 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct return; } + /* + * Ignore reports for absolute data if the data didn't change. This is + * not only an optimization but also fixes 'dead' key reports. Some + * RollOver implementations for localized keys (like BACKSLASH/PIPE; HID + * 0x31 and 0x32) report multiple keys, even though a localized keyboard + * can only have one of them physically available. The 'dead' keys + * report constant 0. As all map to the same keycode, they'd confuse + * the input layer. If we filter the 'dead' keys on the HID level, we + * skip the keycode translation and only forward real events. + */ + if (!(field->flags & (HID_MAIN_ITEM_RELATIVE | + HID_MAIN_ITEM_BUFFERED_BYTE)) && + usage->usage_index < field->maxusage && + value == field->value[usage->usage_index]) + return; + /* report the usage code as scancode if the key status has changed */ if (usage->type == EV_KEY && !!test_bit(usage->code, input->key) != value) input_event(input, EV_MSC, MSC_SCAN, usage->hid); From b60843df9138ccf558e0c67ab015af57b7f2d125 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Tue, 6 Jan 2015 22:34:19 +0100 Subject: [PATCH 323/788] HID: fixup the conflicting keyboard mappings quirk commit 8e7b341037db1835ee6eea64663013cbfcf33575 upstream. The ignore check that got added in 6ce901eb61 ("HID: input: fix confusion on conflicting mappings") needs to properly check for VARIABLE reports as well (ARRAY reports should be ignored), otherwise legitimate keyboards might break. Fixes: 6ce901eb61 ("HID: input: fix confusion on conflicting mappings") Reported-by: Fredrik Hallenberg Reported-by: David Herrmann Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-input.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index cb9c6900df11a2..294d3aea132e8e 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1116,6 +1116,7 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct */ if (!(field->flags & (HID_MAIN_ITEM_RELATIVE | HID_MAIN_ITEM_BUFFERED_BYTE)) && + (field->flags & HID_MAIN_ITEM_VARIABLE) && usage->usage_index < field->maxusage && value == field->value[usage->usage_index]) return; From 556621de97a390c4e70ff1256a94418e79b196cb Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Thu, 22 Jan 2015 15:53:28 -0800 Subject: [PATCH 324/788] HID: wacom: Report ABS_MISC event for Cintiq Companion Hybrid commit 33e5df0e0e32027866e9fb00451952998fc957f2 upstream. It appears that the Cintiq Companion Hybrid does not send an ABS_MISC event to userspace when any of its ExpressKeys are pressed. This is not strictly necessary now that the pad exists on its own device, but should be fixed for consistency's sake. Traditionally both the stylus and pad shared the same device node, and xf86-input-wacom would use ABS_MISC for disambiguation. Not sending this causes the Hybrid to behave incorrectly with xf86-input-wacom beginning with its 8f44f3 commit. Signed-off-by: Jason Gerecke Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/wacom_wac.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index ac7447c7b82e35..e7f35857462c5a 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -760,6 +760,12 @@ static int wacom_intuos_irq(struct wacom_wac *wacom) input_report_key(input, BTN_7, (data[4] & 0x40)); /* Left */ input_report_key(input, BTN_8, (data[4] & 0x80)); /* Down */ input_report_key(input, BTN_0, (data[3] & 0x01)); /* Center */ + + if (data[4] | (data[3] & 0x01)) { + input_report_abs(input, ABS_MISC, PAD_DEVICE_ID); + } else { + input_report_abs(input, ABS_MISC, 0); + } } else if (features->type >= INTUOS5S && features->type <= INTUOSPL) { int i; From ba59d6da4dcd6cefe304350a63aef87912c3fdd5 Mon Sep 17 00:00:00 2001 From: "Nathan-J. Hirschauer" Date: Wed, 18 Feb 2015 02:01:19 +0100 Subject: [PATCH 325/788] drm/radeon: enable native backlight control on old macs commit 7a26f9ad1b5badfd0200ce2262ad696e2a6b7fbb upstream. Commit b7bc596ebbe0 ("drm/radeon: disable native backlight control on pre-r6xx asics (v2)") accidently broke backlight control on old mac laptops that use the on-GPU backlight controller. Signed-off-by: Nathan-J. Hirschauer Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/radeon_encoders.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/radeon/radeon_encoders.c b/drivers/gpu/drm/radeon/radeon_encoders.c index 6b670b0bc47bb9..3a297037cc1762 100644 --- a/drivers/gpu/drm/radeon/radeon_encoders.c +++ b/drivers/gpu/drm/radeon/radeon_encoders.c @@ -179,9 +179,12 @@ static void radeon_encoder_add_backlight(struct radeon_encoder *radeon_encoder, (rdev->pdev->subsystem_vendor == 0x1734) && (rdev->pdev->subsystem_device == 0x1107)) use_bl = false; +/* Older PPC macs use on-GPU backlight controller */ +#ifndef CONFIG_PPC_PMAC /* disable native backlight control on older asics */ else if (rdev->family < CHIP_R600) use_bl = false; +#endif else use_bl = true; } From 2742afe60ceaf837ac915b513be924cab4c2a86d Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 18 Feb 2015 01:05:30 -0500 Subject: [PATCH 326/788] drm/radeon: use drm_mode_vrefresh() rather than mode->vrefresh commit 3d2d98ee1af0cf6eebfbd6bff4c17d3601ac1284 upstream. Just in case it hasn't been calculated for the mode. Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/r600_dpm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/radeon/r600_dpm.c b/drivers/gpu/drm/radeon/r600_dpm.c index 843b65f46ece16..fa2154493cf153 100644 --- a/drivers/gpu/drm/radeon/r600_dpm.c +++ b/drivers/gpu/drm/radeon/r600_dpm.c @@ -188,7 +188,7 @@ u32 r600_dpm_get_vrefresh(struct radeon_device *rdev) list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { radeon_crtc = to_radeon_crtc(crtc); if (crtc->enabled && radeon_crtc->enabled && radeon_crtc->hw_mode.clock) { - vrefresh = radeon_crtc->hw_mode.vrefresh; + vrefresh = drm_mode_vrefresh(&radeon_crtc->hw_mode); break; } } From 6bf1c600c62ba548282f8bb555f622fa27eea2a9 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 19 Feb 2015 16:02:15 -0500 Subject: [PATCH 327/788] drm/radeon: fix 1 RB harvest config setup for TN/RL commit dbfb00c3e7e18439f2ebf67fe99bf7a50b5bae1e upstream. The logic was reversed from what the hw actually exposed. Fixes graphics corruption in certain harvest configurations. Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/ni.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c index aea48c89b24170..88b2c36f8e1f05 100644 --- a/drivers/gpu/drm/radeon/ni.c +++ b/drivers/gpu/drm/radeon/ni.c @@ -1085,12 +1085,12 @@ static void cayman_gpu_init(struct radeon_device *rdev) if ((rdev->config.cayman.max_backends_per_se == 1) && (rdev->flags & RADEON_IS_IGP)) { - if ((disabled_rb_mask & 3) == 1) { - /* RB0 disabled, RB1 enabled */ - tmp = 0x11111111; - } else { + if ((disabled_rb_mask & 3) == 2) { /* RB1 disabled, RB0 enabled */ tmp = 0x00000000; + } else { + /* RB0 disabled, RB1 enabled */ + tmp = 0x11111111; } } else { tmp = gb_addr_config & NUM_PIPES_MASK; From f5c1d274f82501c790c84da92870659aa245f179 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Wed, 21 Jan 2015 11:46:32 -0800 Subject: [PATCH 328/788] drm/i915/bdw: PCI IDs ending in 0xb are ULT. commit 0dc6f20b9803f09726bbb682649d35cda8ef5b5d upstream. When reviewing patch that fixes VGA on BDW Halo Jani noticed that we also had other ULT IDs that weren't listed there. So this follow-up patch add these pci-ids as halo and fix comments on i915_pciids.h Cc: Jani Nikula Signed-off-by: Rodrigo Vivi Signed-off-by: Jani Nikula Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/i915_drv.h | 1 + include/drm/i915_pciids.h | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 9d7a7155bf02a6..0936b0f94826b9 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2160,6 +2160,7 @@ struct drm_i915_cmd_table { (INTEL_DEVID(dev) & 0xFF00) == 0x0C00) #define IS_BDW_ULT(dev) (IS_BROADWELL(dev) && \ ((INTEL_DEVID(dev) & 0xf) == 0x6 || \ + (INTEL_DEVID(dev) & 0xf) == 0xb || \ (INTEL_DEVID(dev) & 0xf) == 0xe)) #define IS_BDW_GT3(dev) (IS_BROADWELL(dev) && \ (INTEL_DEVID(dev) & 0x00F0) == 0x0020) diff --git a/include/drm/i915_pciids.h b/include/drm/i915_pciids.h index 180ad0e6de21dd..d016dc57f0073e 100644 --- a/include/drm/i915_pciids.h +++ b/include/drm/i915_pciids.h @@ -214,9 +214,9 @@ INTEL_VGA_DEVICE((((gt) - 1) << 4) | (id), info) #define _INTEL_BDW_M_IDS(gt, info) \ - _INTEL_BDW_M(gt, 0x1602, info), /* ULT */ \ + _INTEL_BDW_M(gt, 0x1602, info), /* Halo */ \ _INTEL_BDW_M(gt, 0x1606, info), /* ULT */ \ - _INTEL_BDW_M(gt, 0x160B, info), /* Iris */ \ + _INTEL_BDW_M(gt, 0x160B, info), /* ULT */ \ _INTEL_BDW_M(gt, 0x160E, info) /* ULX */ #define _INTEL_BDW_D_IDS(gt, info) \ From 07843963b59e9f1fc5faef9fc7e03b5eb5a89ee4 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 12 Feb 2015 07:53:18 +0000 Subject: [PATCH 329/788] drm/i915: Check obj->vma_list under the struct_mutex commit 6c31a614c43ae274546f736b2a33363e149c3dc2 upstream. When we walk the list of vma, or even for protecting against concurrent framebuffer creation, we must hold the struct_mutex or else a second thread can corrupt the list as we walk it. Fixes regression from commit d7f46fc4e7323887494db13f063a8e59861fefb0 Author: Ben Widawsky Date: Fri Dec 6 14:10:55 2013 -0800 drm/i915: Make pin count per VMA References: https://bugs.freedesktop.org/show_bug.cgi?id=89085 Signed-off-by: Chris Wilson Reviewed-by: Daniel Vetter Signed-off-by: Jani Nikula Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/i915_gem_tiling.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index 4727a4e2c87c98..ffe9072a1a5fc0 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -335,9 +335,10 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, return -EINVAL; } + mutex_lock(&dev->struct_mutex); if (i915_gem_obj_is_pinned(obj) || obj->framebuffer_references) { - drm_gem_object_unreference_unlocked(&obj->base); - return -EBUSY; + ret = -EBUSY; + goto err; } if (args->tiling_mode == I915_TILING_NONE) { @@ -369,7 +370,6 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, } } - mutex_lock(&dev->struct_mutex); if (args->tiling_mode != obj->tiling_mode || args->stride != obj->stride) { /* We need to rebind the object if its current allocation @@ -424,6 +424,7 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, obj->bit_17 = NULL; } +err: drm_gem_object_unreference(&obj->base); mutex_unlock(&dev->struct_mutex); From fcff51b5798e3cdca590e06d7dbb919197ff0608 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 19 Feb 2015 10:53:39 +0200 Subject: [PATCH 330/788] drm/i915: Dell Chromebook 11 has PWM backlight commit cf6f0af9fbdd90b81af14fa6375387131cd8adf1 upstream. Add quirk for Dell Chromebook 11 backlight. Reported-and-tested-by: Owen Garland Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=93451 Acked-by: Damien Lespiau Signed-off-by: Jani Nikula Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/intel_display.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e7a16f119a294d..30d4eb300be07f 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -13019,6 +13019,9 @@ static struct intel_quirk intel_quirks[] = { /* HP Chromebook 14 (Celeron 2955U) */ { 0x0a06, 0x103c, 0x21ed, quirk_backlight_present }, + + /* Dell Chromebook 11 */ + { 0x0a06, 0x1028, 0x0a35, quirk_backlight_present }, }; static void intel_init_quirks(struct drm_device *dev) From fddd92aa4bbf7853240fc2ceb80de2e044e22358 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 24 Feb 2015 11:14:30 +0200 Subject: [PATCH 331/788] drm/i915: avoid processing spurious/shared interrupts in low-power states commit 2dd2a883aad7c852400027c2261bcab69d9e238e upstream. Atm, it's possible that the interrupt handler is called when the device is in D3 or some other low-power state. It can be due to another device that is still in D0 state and shares the interrupt line with i915, or on some platforms there could be spurious interrupts even without sharing the interrupt line. The latter case was reported by Klaus Ethgen using a Lenovo x61p machine (gen 4). He noticed this issue via a system suspend/resume hang and bisected it to the following commit: commit e11aa362308f5de467ce355a2a2471321b15a35c Author: Jesse Barnes Date: Wed Jun 18 09:52:55 2014 -0700 drm/i915: use runtime irq suspend/resume in freeze/thaw This is a problem, since in low-power states IIR will always read 0xffffffff resulting in an endless IRQ servicing loop. Fix this by handling interrupts only when the driver explicitly enables them and so it's guaranteed that the interrupt registers return a valid value. Note that this issue existed even before the above commit, since during runtime suspend/resume we never unregistered the handler. v2: - clarify the purpose of smp_mb() vs. synchronize_irq() in the code comment (Chris) v3: - no need for an explicit smp_mb(), we can assume that synchronize_irq() and the mmio read/writes in the install hooks provide for this (Daniel) - remove code comment as the remaining synchronize_irq() is self explanatory (Daniel) v4: - drm_irq_uninstall() implies synchronize_irq(), so no need to call it explicitly (Daniel) Reference: https://lkml.org/lkml/2015/2/11/205 Reported-and-bisected-by: Klaus Ethgen Signed-off-by: Imre Deak Reviewed-by: Daniel Vetter Signed-off-by: Jani Nikula Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/i915_irq.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index b051a238baf933..1464bc1f89434c 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1884,6 +1884,9 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg) u32 iir, gt_iir, pm_iir; irqreturn_t ret = IRQ_NONE; + if (!intel_irqs_enabled(dev_priv)) + return IRQ_NONE; + while (true) { /* Find, clear, then process each source of interrupt */ @@ -1928,6 +1931,9 @@ static irqreturn_t cherryview_irq_handler(int irq, void *arg) u32 master_ctl, iir; irqreturn_t ret = IRQ_NONE; + if (!intel_irqs_enabled(dev_priv)) + return IRQ_NONE; + for (;;) { master_ctl = I915_READ(GEN8_MASTER_IRQ) & ~GEN8_MASTER_IRQ_CONTROL; iir = I915_READ(VLV_IIR); @@ -2200,6 +2206,9 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) u32 de_iir, gt_iir, de_ier, sde_ier = 0; irqreturn_t ret = IRQ_NONE; + if (!intel_irqs_enabled(dev_priv)) + return IRQ_NONE; + /* We get interrupts on unclaimed registers, so check for this before we * do any I915_{READ,WRITE}. */ intel_uncore_check_errors(dev); @@ -2271,6 +2280,9 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg) enum pipe pipe; u32 aux_mask = GEN8_AUX_CHANNEL_A; + if (!intel_irqs_enabled(dev_priv)) + return IRQ_NONE; + if (IS_GEN9(dev)) aux_mask |= GEN9_AUX_CHANNEL_B | GEN9_AUX_CHANNEL_C | GEN9_AUX_CHANNEL_D; @@ -3770,6 +3782,9 @@ static irqreturn_t i8xx_irq_handler(int irq, void *arg) I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT; + if (!intel_irqs_enabled(dev_priv)) + return IRQ_NONE; + iir = I915_READ16(IIR); if (iir == 0) return IRQ_NONE; @@ -3950,6 +3965,9 @@ static irqreturn_t i915_irq_handler(int irq, void *arg) I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT; int pipe, ret = IRQ_NONE; + if (!intel_irqs_enabled(dev_priv)) + return IRQ_NONE; + iir = I915_READ(IIR); do { bool irq_received = (iir & ~flip_mask) != 0; @@ -4172,6 +4190,9 @@ static irqreturn_t i965_irq_handler(int irq, void *arg) I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT; + if (!intel_irqs_enabled(dev_priv)) + return IRQ_NONE; + iir = I915_READ(IIR); for (;;) { @@ -4523,6 +4544,7 @@ void intel_runtime_pm_disable_interrupts(struct drm_i915_private *dev_priv) { dev_priv->dev->driver->irq_uninstall(dev_priv->dev); dev_priv->pm.irqs_enabled = false; + synchronize_irq(dev_priv->dev->irq); } /** From 9c0f288d39f786bdcaa0c8666e05f1ff747b2aac Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 26 Feb 2015 15:53:02 +0000 Subject: [PATCH 332/788] drm/i915: Check for driver readyness before handling an underrun interrupt commit 54fc7c1c961cb39edfe31f8a3f5ba6414e134b37 upstream. When we takeover from the BIOS and install our interrupt handler, the BIOS may have left us a few surprises in the form of spontaneous interrupts. (This is especially likely on hardware like 965gm where display fifo underruns are continuous and the GMCH cannot filter that interrupt souce.) As we enable our IRQ early so that we can use it during hardware probing, our interrupt handler must be prepared to handle a few sources prior to being fully configured. As such, we need to add a simple is-ready check prior to dereferencing our KMS state for reporting underruns. Reported-by: Rob Clark Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1193972 Signed-off-by: Chris Wilson Reviewed-by: Daniel Vetter [Jani: dropped the extra !] Signed-off-by: Jani Nikula Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/intel_fifo_underrun.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_fifo_underrun.c b/drivers/gpu/drm/i915/intel_fifo_underrun.c index 77af512d2d3506..b16f2cd15cabb8 100644 --- a/drivers/gpu/drm/i915/intel_fifo_underrun.c +++ b/drivers/gpu/drm/i915/intel_fifo_underrun.c @@ -282,16 +282,6 @@ bool intel_set_cpu_fifo_underrun_reporting(struct drm_i915_private *dev_priv, return ret; } -static bool -__cpu_fifo_underrun_reporting_enabled(struct drm_i915_private *dev_priv, - enum pipe pipe) -{ - struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe]; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - - return !intel_crtc->cpu_fifo_underrun_disabled; -} - /** * intel_set_pch_fifo_underrun_reporting - set PCH fifo underrun reporting state * @dev_priv: i915 device instance @@ -352,9 +342,15 @@ bool intel_set_pch_fifo_underrun_reporting(struct drm_i915_private *dev_priv, void intel_cpu_fifo_underrun_irq_handler(struct drm_i915_private *dev_priv, enum pipe pipe) { + struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe]; + + /* We may be called too early in init, thanks BIOS! */ + if (crtc == NULL) + return; + /* GMCH can't disable fifo underruns, filter them. */ if (HAS_GMCH_DISPLAY(dev_priv->dev) && - !__cpu_fifo_underrun_reporting_enabled(dev_priv, pipe)) + to_intel_crtc(crtc)->cpu_fifo_underrun_disabled) return; if (intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, false)) From e00076b74987cf251b6a5788d590b32db9ccb400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Thu, 22 Jan 2015 16:00:17 +0900 Subject: [PATCH 333/788] reservation: Remove shadowing local variable 'ret' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 4eb2440ed60fb5793f7aa6da89b3d517cc59de43 upstream. It was causing the return value of fence_is_signaled to be ignored, making reservation objects signal too early. Reviewed-by: Maarten Lankhorst Reviewed-by: Alex Deucher Signed-off-by: Michel Dänzer Signed-off-by: Sumit Semwal Signed-off-by: Greg Kroah-Hartman --- drivers/dma-buf/reservation.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/dma-buf/reservation.c b/drivers/dma-buf/reservation.c index 3c97c8fa8d0268..8a37af923094ae 100644 --- a/drivers/dma-buf/reservation.c +++ b/drivers/dma-buf/reservation.c @@ -402,8 +402,6 @@ reservation_object_test_signaled_single(struct fence *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; From 1802992eaf26ba6a1fe4e6789cb348f3a69d7170 Mon Sep 17 00:00:00 2001 From: Andrew Elble Date: Wed, 25 Feb 2015 17:46:27 -0500 Subject: [PATCH 334/788] nfsd: fix clp->cl_revoked list deletion causing softlock in nfsd commit c876486be17aeefe0da569f3d111cbd8de6f675d upstream. commit 2d4a532d385f ("nfsd: ensure that clp->cl_revoked list is protected by clp->cl_lock") removed the use of the reaplist to clean out clp->cl_revoked. It failed to change list_entry() to walk clp->cl_revoked.next instead of reaplist.next Fixes: 2d4a532d385f ("nfsd: ensure that clp->cl_revoked list is protected by clp->cl_lock") Reported-by: Eric Meddaugh Tested-by: Eric Meddaugh Signed-off-by: Andrew Elble Reviewed-by: Jeff Layton Signed-off-by: J. Bruce Fields Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4state.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index c06a1ba80d73e5..1685b82a9ccdf6 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1653,7 +1653,7 @@ __destroy_client(struct nfs4_client *clp) nfs4_put_stid(&dp->dl_stid); } while (!list_empty(&clp->cl_revoked)) { - dp = list_entry(reaplist.next, struct nfs4_delegation, dl_recall_lru); + dp = list_entry(clp->cl_revoked.next, struct nfs4_delegation, dl_recall_lru); list_del_init(&dp->dl_recall_lru); nfs4_put_stid(&dp->dl_stid); } From 51716c730b1560c1c79c1d7de6115be10b1eef8a Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 15 Jan 2015 12:21:21 +0300 Subject: [PATCH 335/788] efi: Small leak on error in runtime map code commit 86d68a58d00db3770735b5919ef2c6b12d7f06f3 upstream. The "> 0" here should ">= 0" so we free map_entries[0]. Fixes: 926172d46038 ('efi: Export EFI runtime memory mapping to sysfs') Signed-off-by: Dan Carpenter Acked-by: Dave Young Signed-off-by: Matt Fleming Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/efi/runtime-map.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/efi/runtime-map.c b/drivers/firmware/efi/runtime-map.c index 018c29a2661553..87b8e3b900d219 100644 --- a/drivers/firmware/efi/runtime-map.c +++ b/drivers/firmware/efi/runtime-map.c @@ -191,7 +191,7 @@ int __init efi_runtime_map_init(struct kobject *efi_kobj) return 0; out_add_entry: - for (j = i - 1; j > 0; j--) { + for (j = i - 1; j >= 0; j--) { entry = *(map_entries + j); kobject_put(&entry->kobj); } From 94c601ebbd3949d0bf307a61c35542121d98759b Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 19 Feb 2015 20:18:03 -0800 Subject: [PATCH 336/788] efi/libstub: Fix boundary checking in efi_high_alloc() commit 7ed620bb343f434f8a85f830020c04988df2a140 upstream. While adding support loading kernel and initrd above 4G to grub2 in legacy mode, I was referring to efi_high_alloc(). That will allocate buffer for kernel and then initrd, and initrd will use kernel buffer start as limit. During testing found two buffers will be overlapped when initrd size is very big like 400M. It turns out efi_high_alloc() boundary checking is not right. end - size will be the new start, and should not compare new start with max, we need to make sure end is smaller than max. [ Basically, with the current efi_high_alloc() code it's possible to allocate memory above 'max', because efi_high_alloc() doesn't check that the tail of the allocation is below 'max'. If you have an EFI memory map with a single entry that looks like so, [0xc0000000-0xc0004000] And want to allocate 0x3000 bytes below 0xc0003000 the current code will allocate [0xc0001000-0xc0004000], not [0xc0000000-0xc0003000] like you would expect. - Matt ] Signed-off-by: Yinghai Lu Reviewed-by: Ard Biesheuvel Reviewed-by: Mark Rutland Tested-by: Mark Rutland Signed-off-by: Matt Fleming Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/efi/libstub/efi-stub-helper.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index a920fec8fe8856..5186eb01945a35 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -170,12 +170,12 @@ efi_status_t efi_high_alloc(efi_system_table_t *sys_table_arg, start = desc->phys_addr; end = start + desc->num_pages * (1UL << EFI_PAGE_SHIFT); - if ((start + size) > end || (start + size) > max) - continue; - - if (end - size > max) + if (end > max) end = max; + if ((start + size) > end) + continue; + if (round_down(end - size, align) < start) continue; From 1abae049257e1442679c09729243920cb5e600fd Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Tue, 24 Feb 2015 19:28:10 -0600 Subject: [PATCH 337/788] eCryptfs: don't pass fs-specific ioctl commands through commit 6d65261a09adaa374c05de807f73a144d783669e upstream. eCryptfs can't be aware of what to expect when after passing an arbitrary ioctl command through to the lower filesystem. The ioctl command may trigger an action in the lower filesystem that is incompatible with eCryptfs. One specific example is when one attempts to use the Btrfs clone ioctl command when the source file is in the Btrfs filesystem that eCryptfs is mounted on top of and the destination fd is from a new file created in the eCryptfs mount. The ioctl syscall incorrectly returns success because the command is passed down to Btrfs which thinks that it was able to do the clone operation. However, the result is an empty eCryptfs file. This patch allows the trim, {g,s}etflags, and {g,s}etversion ioctl commands through and then copies up the inode metadata from the lower inode to the eCryptfs inode to catch any changes made to the lower inode's metadata. Those five ioctl commands are mostly common across all filesystems but the whitelist may need to be further pruned in the future. https://bugzilla.kernel.org/show_bug.cgi?id=93691 https://launchpad.net/bugs/1305335 Signed-off-by: Tyler Hicks Cc: Rocko Cc: Colin Ian King Signed-off-by: Greg Kroah-Hartman --- fs/ecryptfs/file.c | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c index 6f4e659f508f30..6058c7c996d639 100644 --- a/fs/ecryptfs/file.c +++ b/fs/ecryptfs/file.c @@ -303,9 +303,22 @@ ecryptfs_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) struct file *lower_file = ecryptfs_file_to_lower(file); long rc = -ENOTTY; - if (lower_file->f_op->unlocked_ioctl) + if (!lower_file->f_op->unlocked_ioctl) + return rc; + + switch (cmd) { + case FITRIM: + case FS_IOC_GETFLAGS: + case FS_IOC_SETFLAGS: + case FS_IOC_GETVERSION: + case FS_IOC_SETVERSION: rc = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg); - return rc; + fsstack_copy_attr_all(file_inode(file), file_inode(lower_file)); + + return rc; + default: + return rc; + } } #ifdef CONFIG_COMPAT @@ -315,9 +328,22 @@ ecryptfs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) struct file *lower_file = ecryptfs_file_to_lower(file); long rc = -ENOIOCTLCMD; - if (lower_file->f_op->compat_ioctl) + if (!lower_file->f_op->compat_ioctl) + return rc; + + switch (cmd) { + case FITRIM: + case FS_IOC32_GETFLAGS: + case FS_IOC32_SETFLAGS: + case FS_IOC32_GETVERSION: + case FS_IOC32_SETVERSION: rc = lower_file->f_op->compat_ioctl(lower_file, cmd, arg); - return rc; + fsstack_copy_attr_all(file_inode(file), file_inode(lower_file)); + + return rc; + default: + return rc; + } } #endif From 371817cdf176cb53b982aeabdf938ee5c06ced1c Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sun, 1 Mar 2015 10:41:37 +0000 Subject: [PATCH 338/788] ACPI / video: Load the module even if ACPI is disabled commit 6e17cb12881ba8d5e456b89f072dc6b70048af36 upstream. i915.ko depends upon the acpi/video.ko module and so refuses to load if ACPI is disabled at runtime if for example the BIOS is broken beyond repair. acpi/video provides an optional service for i915.ko and so we should just allow the modules to load, but do no nothing in order to let the machines boot correctly. Reported-by: Bill Augur Signed-off-by: Chris Wilson Cc: Daniel Vetter Cc: Jani Nikula Acked-by: Aaron Lu [ rjw: Fixed up the new comment in acpi_video_init() ] Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/video.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 032db459370f85..3647ce71154cda 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -2149,6 +2149,17 @@ EXPORT_SYMBOL(acpi_video_unregister_backlight); static int __init acpi_video_init(void) { + /* + * Let the module load even if ACPI is disabled (e.g. due to + * a broken BIOS) so that i915.ko can still be loaded on such + * old systems without an AcpiOpRegion. + * + * acpi_video_register() will report -ENODEV later as well due + * to acpi_disabled when i915.ko tries to register itself afterwards. + */ + if (acpi_disabled) + return 0; + dmi_check_system(video_dmi_table); if (intel_opregion_present()) From c05d74e58cc4a61abf2353ce7eb46c52bb39e91c Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Fri, 6 Mar 2015 15:48:38 +0200 Subject: [PATCH 339/788] ACPI / LPSS: provide con_id for the clkdev commit fcf0789a96777d79d20290e08bf43943a5619387 upstream. Commit 7d78cbefaa (serial: 8250_dw: add ability to handle the peripheral clock) introduces handling for a second clk to 8250_dw.c which is the driver also for LPSS UART. The second clk forces us to provide identifier (con_id) for the clkdev we create. This fixes an issue where 8250_dw.c is getting the same handler for both clocks. Fixes: 7d78cbefaa (serial: 8250_dw: add ability to handle the peripheral clock) Signed-off-by: Heikki Krogerus Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/acpi_lpss.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 7d5880ded78a5f..e7d257aef9fc4e 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -65,6 +65,7 @@ struct lpss_private_data; struct lpss_device_desc { unsigned int flags; + const char *clk_con_id; unsigned int prv_offset; size_t prv_size_override; void (*setup)(struct lpss_private_data *pdata); @@ -140,6 +141,7 @@ static struct lpss_device_desc lpt_i2c_dev_desc = { static struct lpss_device_desc lpt_uart_dev_desc = { .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR, + .clk_con_id = "baudclk", .prv_offset = 0x800, .setup = lpss_uart_setup, }; @@ -156,6 +158,7 @@ static struct lpss_device_desc byt_pwm_dev_desc = { static struct lpss_device_desc byt_uart_dev_desc = { .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX, + .clk_con_id = "baudclk", .prv_offset = 0x800, .setup = lpss_uart_setup, }; @@ -313,7 +316,7 @@ static int register_device_clock(struct acpi_device *adev, return PTR_ERR(clk); pdata->clk = clk; - clk_register_clkdev(clk, NULL, devname); + clk_register_clkdev(clk, dev_desc->clk_con_id, devname); return 0; } From 6b567d6586968a685389bfca0f4101eb08a4da33 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sun, 22 Feb 2015 16:35:36 -0500 Subject: [PATCH 340/788] NFS: Don't invalidate a submounted dentry in nfs_prime_dcache() commit 6c441c254eea2354d686be7f5544bcd79fb6a61f upstream. If we're traversing a directory which contains a submounted filesystem, or one that has a referral, the NFS server that is processing the READDIR request will often return information for the underlying (mounted-on) directory. It may, or may not, also return filehandle information. If this happens, and the lookup in nfs_prime_dcache() returns the dentry for the submounted directory, the filehandle comparison will fail, and we call d_invalidate(). Post-commit 8ed936b5671bf ("vfs: Lazily remove mounts on unlinked files and directories."), this means the entire subtree is unmounted. The following minimal patch addresses this problem by punting on the invalidation if there is a submount. Kudos to Neil Brown for having tracked down this issue (see link). Reported-by: Nix Link: http://lkml.kernel.org/r/87iofju9ht.fsf@spindle.srvr.nix Signed-off-by: Trond Myklebust Signed-off-by: Greg Kroah-Hartman --- fs/nfs/dir.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 9b0c55cb2a2eaa..4ad7fff9ccaf7d 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -469,6 +469,8 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry) struct inode *inode; int status; + if (!(entry->fattr->valid & NFS_ATTR_FATTR_FSID)) + return; if (filename.name[0] == '.') { if (filename.len == 1) return; @@ -479,6 +481,10 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry) dentry = d_lookup(parent, &filename); if (dentry != NULL) { + /* Is there a mountpoint here? If so, just exit */ + if (!nfs_fsid_equal(&NFS_SB(dentry->d_sb)->fsid, + &entry->fattr->fsid)) + goto out; if (nfs_same_file(dentry, entry)) { nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); status = nfs_refresh_inode(dentry->d_inode, entry->fattr); From e30b85e87ad77f9388801002a6b06679fbc7a2de Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 26 Feb 2015 12:54:46 -0500 Subject: [PATCH 341/788] NFSv4: Don't call put_rpccred() under the rcu_read_lock() commit 7c0af9ffb7bb4e5355470fa60b3eb711ddf226fa upstream. put_rpccred() can sleep. Fixes: 8f649c3762547 ("NFSv4: Fix the locking in nfs_inode_reclaim_delegation()") Signed-off-by: Trond Myklebust Signed-off-by: Greg Kroah-Hartman --- fs/nfs/delegation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 7f3f6064134443..4030b558b07e3d 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -177,8 +177,8 @@ void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, &delegation->flags); NFS_I(inode)->delegation_state = delegation->type; spin_unlock(&delegation->lock); - put_rpccred(oldcred); rcu_read_unlock(); + put_rpccred(oldcred); trace_nfs4_reclaim_delegation(inode, res->delegation_type); } else { /* We appear to have raced with a delegation return. */ From 770728324034609df329e0622f5d474ac91889b0 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 3 Mar 2015 13:38:14 +0200 Subject: [PATCH 342/788] ASoC: omap-pcm: Correct dma mask commit d51199a83a2cf82a291d19ee852c44caa511427d upstream. DMA_BIT_MASK of 64 is not valid dma address mask for OMAPs, it should be set to 32. The 64 was introduced by commit (in 2009): a152ff24b978 ASoC: OMAP: Make DMA 64 aligned But the dma_mask and coherent_dma_mask can not be used to specify alignment. Fixes: a152ff24b978 (ASoC: OMAP: Make DMA 64 aligned) Reported-by: Grygorii Strashko Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/omap/omap-pcm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c index f4b05bc23e4bfb..1343ecbf0bd5ee 100644 --- a/sound/soc/omap/omap-pcm.c +++ b/sound/soc/omap/omap-pcm.c @@ -201,7 +201,7 @@ static int omap_pcm_new(struct snd_soc_pcm_runtime *rtd) struct snd_pcm *pcm = rtd->pcm; int ret; - ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(64)); + ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); if (ret) return ret; From ca729fdf691420fac264199140519c2f746d74c9 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 16 Feb 2015 13:06:45 +0800 Subject: [PATCH 343/788] ASoC: rt5670: Set RT5670_IRQ_CTRL1 non volatile commit 850529249d7cce02e9bfae9476d09c8c51410d28 upstream. RT5670_IRQ_CTRL1(0xbd) is a non volatile register. And we need to restore its value after suspend/resume. Signed-off-by: Bard Liao Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/rt5670.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index 1e574c85b9c8f2..f4b869c638cc45 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -223,7 +223,6 @@ static bool rt5670_volatile_register(struct device *dev, unsigned int reg) case RT5670_ADC_EQ_CTRL1: case RT5670_EQ_CTRL1: case RT5670_ALC_CTRL_1: - case RT5670_IRQ_CTRL1: case RT5670_IRQ_CTRL2: case RT5670_INT_IRQ_ST: case RT5670_IL_CMD: From 66719267d8f5746951c407296d75f5d535a2a24d Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Thu, 18 Dec 2014 14:55:53 -0800 Subject: [PATCH 344/788] stable_kernel_rules: reorganize and update submission options commit 5de61e7aa1ba9ac3c7edbea375da2bc8eb1a89ae upstream. The current organization of Documentation/stable_kernel_rules.txt doesn't clearly differentiate the mutually exclusive options for submission to the -stable review process. As I understand it, patches are not actually required to be mailed directly to stable@vger.kernel.org, but the instructions do not make this clear. Also, there are some established processes that are not listed -- specifically, what I call Option 2 below. This patch updates and reorganizes a bit, to make things clearer. Signed-off-by: Brian Norris Signed-off-by: Greg Kroah-Hartman --- Documentation/stable_kernel_rules.txt | 44 +++++++++++++++++++++------ 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/Documentation/stable_kernel_rules.txt b/Documentation/stable_kernel_rules.txt index aee73e78c7d42a..02f8331edb8bfd 100644 --- a/Documentation/stable_kernel_rules.txt +++ b/Documentation/stable_kernel_rules.txt @@ -32,18 +32,42 @@ 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 - it to be applied to. - - To have the patch automatically included in the stable tree, add the tag + - Security patches should not be handled (solely) by the -stable review + process but should follow the procedures in Documentation/SecurityBugs. + +For all other submissions, choose one of the following procedures: + + --- Option 1 --- + + To have the patch automatically included in the stable tree, add the tag Cc: stable@vger.kernel.org in the sign-off area. Once the patch is merged it will be applied to the stable tree without anything else needing to be done by the author or subsystem maintainer. - - If the patch requires other patches as prerequisites which can be - cherry-picked, then this can be specified in the following format in - the sign-off area: + + --- Option 2 --- + + After the patch has been merged to Linus' tree, send an email to + stable@vger.kernel.org containing the subject of the patch, the commit ID, + why you think it should be applied, and what kernel version you wish it to + be applied to. + + --- Option 3 --- + + 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 + it to be applied to. + +Option 1 is probably the easiest and most common. Options 2 and 3 are more +useful if the patch isn't deemed worthy at the time it is applied to a public +git tree (for instance, because it deserves more regression testing first). +Option 3 is especially useful if the patch needs some special handling to apply +to an older kernel (e.g., if API's have changed in the meantime). + +Additionally, some patches submitted via Option 1 may have additional patch +prerequisites which can be cherry-picked. This can be specified in the following +format in the sign-off area: Cc: # 3.3.x: a1f84a3: sched: Check for idle Cc: # 3.3.x: 1b9508f: sched: Rate-limit newidle @@ -57,13 +81,13 @@ Procedure for submitting patches to the -stable tree: git cherry-pick fd21073 git cherry-pick +Following the submission: + - The sender will receive an ACK when the patch has been accepted into the queue, or a NAK if the patch is rejected. This response might take a few days, according to the developer's schedules. - If accepted, the patch will be added to the -stable queue, for review by other developers and by the relevant subsystem maintainer. - - Security patches should not be sent to this alias, but instead to the - documented security@kernel.org address. Review cycle: From d55e0b17994aa9b2aaefe1671b9ef911e16c0384 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 9 Jan 2015 16:57:12 -0700 Subject: [PATCH 345/788] coresight-etm: unlock on error paths in mode_store() commit 6ad1095990328e7e4b3a0e260825ad4b6406785a upstream. There are some missing unlocks on the error paths. Fixes: a939fc5a71ad ('coresight-etm: add CoreSight ETM/PTM driver') Signed-off-by: Dan Carpenter Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/coresight/coresight-etm3x.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/coresight/coresight-etm3x.c b/drivers/coresight/coresight-etm3x.c index d9e3ed6aa857b4..369cac00afaa46 100644 --- a/drivers/coresight/coresight-etm3x.c +++ b/drivers/coresight/coresight-etm3x.c @@ -573,7 +573,8 @@ static ssize_t mode_store(struct device *dev, if (drvdata->mode & ETM_MODE_STALL) { if (!(drvdata->etmccr & ETMCCR_FIFOFULL)) { dev_warn(drvdata->dev, "stall mode not supported\n"); - return -EINVAL; + ret = -EINVAL; + goto err_unlock; } drvdata->ctrl |= ETMCR_STALL_MODE; } else @@ -582,7 +583,8 @@ static ssize_t mode_store(struct device *dev, if (drvdata->mode & ETM_MODE_TIMESTAMP) { if (!(drvdata->etmccer & ETMCCER_TIMESTAMP)) { dev_warn(drvdata->dev, "timestamp not supported\n"); - return -EINVAL; + ret = -EINVAL; + goto err_unlock; } drvdata->ctrl |= ETMCR_TIMESTAMP_EN; } else @@ -595,6 +597,10 @@ static ssize_t mode_store(struct device *dev, spin_unlock(&drvdata->spinlock); return size; + +err_unlock: + spin_unlock(&drvdata->spinlock); + return ret; } static DEVICE_ATTR_RW(mode); From 3d51da2c0217b18a1a408b1f8b1df1a9a2f6662f Mon Sep 17 00:00:00 2001 From: Wanpeng Li Date: Wed, 26 Nov 2014 08:44:06 +0800 Subject: [PATCH 346/788] sched: Fix hrtick_start() on UP commit 868933359a3bdda25b562e9d41bce7071edc1b08 upstream. The commit 177ef2a6315e ("sched/deadline: Fix a precision problem in the microseconds range") forgot to change the UP version of hrtick_start(), do so now. Signed-off-by: Wanpeng Li Fixes: 177ef2a6315e ("sched/deadline: Fix a precision problem in the microseconds range") [ Fixed the changelog. ] Signed-off-by: Peter Zijlstra (Intel) Cc: Juri Lelli Cc: Kirill Tkhai Cc: Linus Torvalds Link: http://lkml.kernel.org/r/1416962647-76792-7-git-send-email-wanpeng.li@linux.intel.com Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- kernel/sched/core.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 5eab11d4b747df..48c50118b17a59 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -490,6 +490,11 @@ static __init void init_hrtick(void) */ void hrtick_start(struct rq *rq, u64 delay) { + /* + * Don't schedule slices shorter than 10000ns, that just + * doesn't make sense. Rely on vruntime for fairness. + */ + delay = max_t(u64, delay, 10000LL); __hrtimer_start_range_ns(&rq->hrtick_timer, ns_to_ktime(delay), 0, HRTIMER_MODE_REL_PINNED, 0); } From 2ac80c2fd4214e398b010ff52683924de81c88f5 Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Tue, 27 Jan 2015 18:01:45 +0000 Subject: [PATCH 347/788] of/pci: Free resources on failure in of_pci_get_host_bridge_resources() commit d2be00c0fb5ae0794deffcdb0425cd5a8d823db0 upstream. In the function of_pci_get_host_bridge_resources() if the parsing of ranges fails, previously allocated resources inclusive of bus_range are not freed and are not expected to be freed by the function caller on error return. This patch fixes the issues by adding code that properly frees resources and bus_range before exiting the function with an error return value. Fixes: cbe4097f8ae6 ("of/pci: Add support for parsing PCI host bridge resources from DT") Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Acked-by: Liviu Dudau CC: Arnd Bergmann CC: Rob Herring Signed-off-by: Greg Kroah-Hartman --- drivers/of/of_pci.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c index 88471d3d98cd6c..60dc36c865b5d7 100644 --- a/drivers/of/of_pci.c +++ b/drivers/of/of_pci.c @@ -140,6 +140,7 @@ int of_pci_get_host_bridge_resources(struct device_node *dev, unsigned char busno, unsigned char bus_max, struct list_head *resources, resource_size_t *io_base) { + struct pci_host_bridge_window *window; struct resource *res; struct resource *bus_range; struct of_pci_range range; @@ -225,7 +226,10 @@ int of_pci_get_host_bridge_resources(struct device_node *dev, conversion_failed: kfree(res); parse_failed: + list_for_each_entry(window, resources, list) + kfree(window->res); pci_free_resource_list(resources); + kfree(bus_range); return err; } EXPORT_SYMBOL_GPL(of_pci_get_host_bridge_resources); From 3449e4060ca5378d21a1536729797314ae6aa405 Mon Sep 17 00:00:00 2001 From: Andrew Elble Date: Mon, 9 Feb 2015 12:53:04 -0500 Subject: [PATCH 348/788] GFS2: Fix crash during ACL deletion in acl max entry check in gfs2_set_acl() commit 278702074ff77b1a3fa2061267997095959f5e2c upstream. Fixes: e01580bf9e ("gfs2: use generic posix ACL infrastructure") Reported-by: Eric Meddaugh Tested-by: Eric Meddaugh Signed-off-by: Andrew Elble Signed-off-by: Steven Whitehouse Signed-off-by: Greg Kroah-Hartman --- fs/gfs2/acl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/gfs2/acl.c b/fs/gfs2/acl.c index 3088e2a38e3018..7b3143064af113 100644 --- a/fs/gfs2/acl.c +++ b/fs/gfs2/acl.c @@ -73,7 +73,7 @@ int gfs2_set_acl(struct inode *inode, struct posix_acl *acl, int type) BUG_ON(name == NULL); - if (acl->a_count > GFS2_ACL_MAX_ENTRIES(GFS2_SB(inode))) + if (acl && acl->a_count > GFS2_ACL_MAX_ENTRIES(GFS2_SB(inode))) return -E2BIG; if (type == ACL_TYPE_ACCESS) { From 5f98283000a62ec475de3d9a3f2080d1a73e865c Mon Sep 17 00:00:00 2001 From: Sergey Ryazanov Date: Wed, 4 Feb 2015 00:21:13 +0300 Subject: [PATCH 349/788] ath5k: fix spontaneus AR5312 freezes commit 8bfae4f9938b6c1f033a5159febe97e441d6d526 upstream. Sometimes while CPU have some load and ath5k doing the wireless interface reset the whole WiSoC completely freezes. Set of tests shows that using atomic delay function while we wait interface reset helps to avoid such freezes. The easiest way to reproduce this issue: create a station interface, start continous scan with wpa_supplicant and load CPU by something. Or just create multiple station interfaces and put them all in continous scan. This patch partially reverts the commit 1846ac3dbec0 ("ath5k: Use usleep_range where possible"), which replaces initial udelay() by usleep_range(). I do not know actual source of this issue, but all looks like that HW freeze is caused by transaction on internal SoC bus, while wireless block is in reset state. Also I should note that I do not know how many chips are affected, but I did not see this issue with chips, other than AR5312. CC: Jiri Slaby CC: Nick Kossifidis CC: Luis R. Rodriguez Fixes: 1846ac3dbec0 ("ath5k: Use usleep_range where possible") Reported-by: Christophe Prevotaux Tested-by: Christophe Prevotaux Tested-by: Eric Bree Signed-off-by: Sergey Ryazanov Signed-off-by: Kalle Valo Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath5k/reset.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c index a3399c4f13a9a8..b9b651ea985156 100644 --- a/drivers/net/wireless/ath/ath5k/reset.c +++ b/drivers/net/wireless/ath/ath5k/reset.c @@ -478,7 +478,7 @@ ath5k_hw_wisoc_reset(struct ath5k_hw *ah, u32 flags) regval = ioread32(reg); iowrite32(regval | val, reg); regval = ioread32(reg); - usleep_range(100, 150); + udelay(100); /* NB: should be atomic */ /* Bring BB/MAC out of reset */ iowrite32(regval & ~val, reg); From b8a2454e51c75fff6a621bdb5a399aab062d956e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 27 Jan 2015 23:50:25 +0100 Subject: [PATCH 350/788] pinctrl: pinctrl-imx: don't use invalid value of conf_reg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 4ff0f034e95d65f8f063a362dfcf86e986377a82 upstream. The right check for conf_reg to be invalid it testing against -1 not 0 as is done in the rest of the driver. This fixes an oops that can be triggered by: cat /sys/kernel/debug/pinctrl/43fac000.iomuxc/* Fixes: ae75ff814538 ("pinctrl: pinctrl-imx: add imx pinctrl core driver") Signed-off-by: Uwe Kleine-König Signed-off-by: Linus Walleij Signed-off-by: Greg Kroah-Hartman --- drivers/pinctrl/freescale/pinctrl-imx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/freescale/pinctrl-imx.c b/drivers/pinctrl/freescale/pinctrl-imx.c index 52f2b9404fe058..448f10986c2849 100644 --- a/drivers/pinctrl/freescale/pinctrl-imx.c +++ b/drivers/pinctrl/freescale/pinctrl-imx.c @@ -437,7 +437,7 @@ static void imx_pinconf_dbg_show(struct pinctrl_dev *pctldev, const struct imx_pin_reg *pin_reg = &info->pin_regs[pin_id]; unsigned long config; - if (!pin_reg || !pin_reg->conf_reg) { + if (!pin_reg || pin_reg->conf_reg == -1) { seq_printf(s, "N/A"); return; } From 97df643ce1f1aa7e237bc7c51b6d6fcd848e88fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 28 Jan 2015 00:45:56 +0100 Subject: [PATCH 351/788] pinctrl: imx25: fix numbering for pins MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 34027ca2bbc6043fea8fc5c4a82670518b6be7df upstream. The pin id for a given tuple listed in a fsl,pins property is calculated by dividing the first entry (which is also a register offset) by 4. As the first available register is at offset 0x8 and configures the pad MX25_PAD_A10 the right id for this pin is 2. All other pins are off by one, too. This patch drops the definition MX25_PAD_RESERVE1 (together with its only use) and decrements all following values by 1. Fixes: b4a87c9b966f ("pinctrl: pinctrl-imx: add imx25 pinctrl driver") Signed-off-by: Uwe Kleine-König Tested-by: Fabio Estevam Signed-off-by: Linus Walleij Signed-off-by: Greg Kroah-Hartman --- drivers/pinctrl/freescale/pinctrl-imx25.c | 276 +++++++++++----------- 1 file changed, 137 insertions(+), 139 deletions(-) diff --git a/drivers/pinctrl/freescale/pinctrl-imx25.c b/drivers/pinctrl/freescale/pinctrl-imx25.c index 8d1013a040c910..faf635654312a7 100644 --- a/drivers/pinctrl/freescale/pinctrl-imx25.c +++ b/drivers/pinctrl/freescale/pinctrl-imx25.c @@ -27,150 +27,148 @@ enum imx25_pads { MX25_PAD_RESERVE0 = 1, - MX25_PAD_RESERVE1 = 2, - MX25_PAD_A10 = 3, - MX25_PAD_A13 = 4, - MX25_PAD_A14 = 5, - MX25_PAD_A15 = 6, - MX25_PAD_A16 = 7, - MX25_PAD_A17 = 8, - MX25_PAD_A18 = 9, - MX25_PAD_A19 = 10, - MX25_PAD_A20 = 11, - MX25_PAD_A21 = 12, - MX25_PAD_A22 = 13, - MX25_PAD_A23 = 14, - MX25_PAD_A24 = 15, - MX25_PAD_A25 = 16, - MX25_PAD_EB0 = 17, - MX25_PAD_EB1 = 18, - MX25_PAD_OE = 19, - MX25_PAD_CS0 = 20, - MX25_PAD_CS1 = 21, - MX25_PAD_CS4 = 22, - MX25_PAD_CS5 = 23, - MX25_PAD_NF_CE0 = 24, - MX25_PAD_ECB = 25, - MX25_PAD_LBA = 26, - MX25_PAD_BCLK = 27, - MX25_PAD_RW = 28, - MX25_PAD_NFWE_B = 29, - MX25_PAD_NFRE_B = 30, - MX25_PAD_NFALE = 31, - MX25_PAD_NFCLE = 32, - MX25_PAD_NFWP_B = 33, - MX25_PAD_NFRB = 34, - MX25_PAD_D15 = 35, - MX25_PAD_D14 = 36, - MX25_PAD_D13 = 37, - MX25_PAD_D12 = 38, - MX25_PAD_D11 = 39, - MX25_PAD_D10 = 40, - MX25_PAD_D9 = 41, - MX25_PAD_D8 = 42, - MX25_PAD_D7 = 43, - MX25_PAD_D6 = 44, - MX25_PAD_D5 = 45, - MX25_PAD_D4 = 46, - MX25_PAD_D3 = 47, - MX25_PAD_D2 = 48, - MX25_PAD_D1 = 49, - MX25_PAD_D0 = 50, - MX25_PAD_LD0 = 51, - MX25_PAD_LD1 = 52, - MX25_PAD_LD2 = 53, - MX25_PAD_LD3 = 54, - MX25_PAD_LD4 = 55, - MX25_PAD_LD5 = 56, - MX25_PAD_LD6 = 57, - MX25_PAD_LD7 = 58, - MX25_PAD_LD8 = 59, - MX25_PAD_LD9 = 60, - MX25_PAD_LD10 = 61, - MX25_PAD_LD11 = 62, - MX25_PAD_LD12 = 63, - MX25_PAD_LD13 = 64, - MX25_PAD_LD14 = 65, - MX25_PAD_LD15 = 66, - MX25_PAD_HSYNC = 67, - MX25_PAD_VSYNC = 68, - MX25_PAD_LSCLK = 69, - MX25_PAD_OE_ACD = 70, - MX25_PAD_CONTRAST = 71, - MX25_PAD_PWM = 72, - MX25_PAD_CSI_D2 = 73, - MX25_PAD_CSI_D3 = 74, - MX25_PAD_CSI_D4 = 75, - MX25_PAD_CSI_D5 = 76, - MX25_PAD_CSI_D6 = 77, - MX25_PAD_CSI_D7 = 78, - MX25_PAD_CSI_D8 = 79, - MX25_PAD_CSI_D9 = 80, - MX25_PAD_CSI_MCLK = 81, - MX25_PAD_CSI_VSYNC = 82, - MX25_PAD_CSI_HSYNC = 83, - MX25_PAD_CSI_PIXCLK = 84, - MX25_PAD_I2C1_CLK = 85, - MX25_PAD_I2C1_DAT = 86, - MX25_PAD_CSPI1_MOSI = 87, - MX25_PAD_CSPI1_MISO = 88, - MX25_PAD_CSPI1_SS0 = 89, - MX25_PAD_CSPI1_SS1 = 90, - MX25_PAD_CSPI1_SCLK = 91, - MX25_PAD_CSPI1_RDY = 92, - MX25_PAD_UART1_RXD = 93, - MX25_PAD_UART1_TXD = 94, - MX25_PAD_UART1_RTS = 95, - MX25_PAD_UART1_CTS = 96, - MX25_PAD_UART2_RXD = 97, - MX25_PAD_UART2_TXD = 98, - MX25_PAD_UART2_RTS = 99, - MX25_PAD_UART2_CTS = 100, - MX25_PAD_SD1_CMD = 101, - MX25_PAD_SD1_CLK = 102, - MX25_PAD_SD1_DATA0 = 103, - MX25_PAD_SD1_DATA1 = 104, - MX25_PAD_SD1_DATA2 = 105, - MX25_PAD_SD1_DATA3 = 106, - MX25_PAD_KPP_ROW0 = 107, - MX25_PAD_KPP_ROW1 = 108, - MX25_PAD_KPP_ROW2 = 109, - MX25_PAD_KPP_ROW3 = 110, - MX25_PAD_KPP_COL0 = 111, - MX25_PAD_KPP_COL1 = 112, - MX25_PAD_KPP_COL2 = 113, - MX25_PAD_KPP_COL3 = 114, - MX25_PAD_FEC_MDC = 115, - MX25_PAD_FEC_MDIO = 116, - MX25_PAD_FEC_TDATA0 = 117, - MX25_PAD_FEC_TDATA1 = 118, - MX25_PAD_FEC_TX_EN = 119, - MX25_PAD_FEC_RDATA0 = 120, - MX25_PAD_FEC_RDATA1 = 121, - MX25_PAD_FEC_RX_DV = 122, - MX25_PAD_FEC_TX_CLK = 123, - MX25_PAD_RTCK = 124, - MX25_PAD_DE_B = 125, - MX25_PAD_GPIO_A = 126, - MX25_PAD_GPIO_B = 127, - MX25_PAD_GPIO_C = 128, - MX25_PAD_GPIO_D = 129, - MX25_PAD_GPIO_E = 130, - MX25_PAD_GPIO_F = 131, - MX25_PAD_EXT_ARMCLK = 132, - MX25_PAD_UPLL_BYPCLK = 133, - MX25_PAD_VSTBY_REQ = 134, - MX25_PAD_VSTBY_ACK = 135, - MX25_PAD_POWER_FAIL = 136, - MX25_PAD_CLKO = 137, - MX25_PAD_BOOT_MODE0 = 138, - MX25_PAD_BOOT_MODE1 = 139, + MX25_PAD_A10 = 2, + MX25_PAD_A13 = 3, + MX25_PAD_A14 = 4, + MX25_PAD_A15 = 5, + MX25_PAD_A16 = 6, + MX25_PAD_A17 = 7, + MX25_PAD_A18 = 8, + MX25_PAD_A19 = 9, + MX25_PAD_A20 = 10, + MX25_PAD_A21 = 11, + MX25_PAD_A22 = 12, + MX25_PAD_A23 = 13, + MX25_PAD_A24 = 14, + MX25_PAD_A25 = 15, + MX25_PAD_EB0 = 16, + MX25_PAD_EB1 = 17, + MX25_PAD_OE = 18, + MX25_PAD_CS0 = 19, + MX25_PAD_CS1 = 20, + MX25_PAD_CS4 = 21, + MX25_PAD_CS5 = 22, + MX25_PAD_NF_CE0 = 23, + MX25_PAD_ECB = 24, + MX25_PAD_LBA = 25, + MX25_PAD_BCLK = 26, + MX25_PAD_RW = 27, + MX25_PAD_NFWE_B = 28, + MX25_PAD_NFRE_B = 29, + MX25_PAD_NFALE = 30, + MX25_PAD_NFCLE = 31, + MX25_PAD_NFWP_B = 32, + MX25_PAD_NFRB = 33, + MX25_PAD_D15 = 34, + MX25_PAD_D14 = 35, + MX25_PAD_D13 = 36, + MX25_PAD_D12 = 37, + MX25_PAD_D11 = 38, + MX25_PAD_D10 = 39, + MX25_PAD_D9 = 40, + MX25_PAD_D8 = 41, + MX25_PAD_D7 = 42, + MX25_PAD_D6 = 43, + MX25_PAD_D5 = 44, + MX25_PAD_D4 = 45, + MX25_PAD_D3 = 46, + MX25_PAD_D2 = 47, + MX25_PAD_D1 = 48, + MX25_PAD_D0 = 49, + MX25_PAD_LD0 = 50, + MX25_PAD_LD1 = 51, + MX25_PAD_LD2 = 52, + MX25_PAD_LD3 = 53, + MX25_PAD_LD4 = 54, + MX25_PAD_LD5 = 55, + MX25_PAD_LD6 = 56, + MX25_PAD_LD7 = 57, + MX25_PAD_LD8 = 58, + MX25_PAD_LD9 = 59, + MX25_PAD_LD10 = 60, + MX25_PAD_LD11 = 61, + MX25_PAD_LD12 = 62, + MX25_PAD_LD13 = 63, + MX25_PAD_LD14 = 64, + MX25_PAD_LD15 = 65, + MX25_PAD_HSYNC = 66, + MX25_PAD_VSYNC = 67, + MX25_PAD_LSCLK = 68, + MX25_PAD_OE_ACD = 69, + MX25_PAD_CONTRAST = 70, + MX25_PAD_PWM = 71, + MX25_PAD_CSI_D2 = 72, + MX25_PAD_CSI_D3 = 73, + MX25_PAD_CSI_D4 = 74, + MX25_PAD_CSI_D5 = 75, + MX25_PAD_CSI_D6 = 76, + MX25_PAD_CSI_D7 = 77, + MX25_PAD_CSI_D8 = 78, + MX25_PAD_CSI_D9 = 79, + MX25_PAD_CSI_MCLK = 80, + MX25_PAD_CSI_VSYNC = 81, + MX25_PAD_CSI_HSYNC = 82, + MX25_PAD_CSI_PIXCLK = 83, + MX25_PAD_I2C1_CLK = 84, + MX25_PAD_I2C1_DAT = 85, + MX25_PAD_CSPI1_MOSI = 86, + MX25_PAD_CSPI1_MISO = 87, + MX25_PAD_CSPI1_SS0 = 88, + MX25_PAD_CSPI1_SS1 = 89, + MX25_PAD_CSPI1_SCLK = 90, + MX25_PAD_CSPI1_RDY = 91, + MX25_PAD_UART1_RXD = 92, + MX25_PAD_UART1_TXD = 93, + MX25_PAD_UART1_RTS = 94, + MX25_PAD_UART1_CTS = 95, + MX25_PAD_UART2_RXD = 96, + MX25_PAD_UART2_TXD = 97, + MX25_PAD_UART2_RTS = 98, + MX25_PAD_UART2_CTS = 99, + MX25_PAD_SD1_CMD = 100, + MX25_PAD_SD1_CLK = 101, + MX25_PAD_SD1_DATA0 = 102, + MX25_PAD_SD1_DATA1 = 103, + MX25_PAD_SD1_DATA2 = 104, + MX25_PAD_SD1_DATA3 = 105, + MX25_PAD_KPP_ROW0 = 106, + MX25_PAD_KPP_ROW1 = 107, + MX25_PAD_KPP_ROW2 = 108, + MX25_PAD_KPP_ROW3 = 109, + MX25_PAD_KPP_COL0 = 110, + MX25_PAD_KPP_COL1 = 111, + MX25_PAD_KPP_COL2 = 112, + MX25_PAD_KPP_COL3 = 113, + MX25_PAD_FEC_MDC = 114, + MX25_PAD_FEC_MDIO = 115, + MX25_PAD_FEC_TDATA0 = 116, + MX25_PAD_FEC_TDATA1 = 117, + MX25_PAD_FEC_TX_EN = 118, + MX25_PAD_FEC_RDATA0 = 119, + MX25_PAD_FEC_RDATA1 = 120, + MX25_PAD_FEC_RX_DV = 121, + MX25_PAD_FEC_TX_CLK = 122, + MX25_PAD_RTCK = 123, + MX25_PAD_DE_B = 124, + MX25_PAD_GPIO_A = 125, + MX25_PAD_GPIO_B = 126, + MX25_PAD_GPIO_C = 127, + MX25_PAD_GPIO_D = 128, + MX25_PAD_GPIO_E = 129, + MX25_PAD_GPIO_F = 130, + MX25_PAD_EXT_ARMCLK = 131, + MX25_PAD_UPLL_BYPCLK = 132, + MX25_PAD_VSTBY_REQ = 133, + MX25_PAD_VSTBY_ACK = 134, + MX25_PAD_POWER_FAIL = 135, + MX25_PAD_CLKO = 136, + MX25_PAD_BOOT_MODE0 = 137, + MX25_PAD_BOOT_MODE1 = 138, }; /* Pad names for the pinmux subsystem */ static const struct pinctrl_pin_desc imx25_pinctrl_pads[] = { IMX_PINCTRL_PIN(MX25_PAD_RESERVE0), - IMX_PINCTRL_PIN(MX25_PAD_RESERVE1), IMX_PINCTRL_PIN(MX25_PAD_A10), IMX_PINCTRL_PIN(MX25_PAD_A13), IMX_PINCTRL_PIN(MX25_PAD_A14), From abe2114079571ffcbb410a3b427e528674526a1b Mon Sep 17 00:00:00 2001 From: Michal Hocko Date: Wed, 11 Feb 2015 15:28:24 -0800 Subject: [PATCH 352/788] vmstat: do not use deferrable delayed work for vmstat_update commit ba4877b9ca51f80b5d30f304a46762f0509e1635 upstream. Vinayak Menon has reported that an excessive number of tasks was throttled in the direct reclaim inside too_many_isolated() because NR_ISOLATED_FILE was relatively high compared to NR_INACTIVE_FILE. However it turned out that the real number of NR_ISOLATED_FILE was 0 and the per-cpu vm_stat_diff wasn't transferred into the global counter. vmstat_work which is responsible for the sync is defined as deferrable delayed work which means that the defined timeout doesn't wake up an idle CPU. A CPU might stay in an idle state for a long time and general effort is to keep such a CPU in this state as long as possible which might lead to all sorts of troubles for vmstat consumers as can be seen with the excessive direct reclaim throttling. This patch basically reverts 39bf6270f524 ("VM statistics: Make timer deferrable") but it shouldn't cause any problems for idle CPUs because only CPUs with an active per-cpu drift are woken up since 7cc36bbddde5 ("vmstat: on-demand vmstat workers v8") and CPUs which are idle for a longer time shouldn't have per-cpu drift. Fixes: 39bf6270f524 (VM statistics: Make timer deferrable) Signed-off-by: Michal Hocko Reported-by: Vinayak Menon Acked-by: Christoph Lameter Cc: Johannes Weiner Cc: Vladimir Davydov Cc: Mel Gorman Cc: Minchan Kim Cc: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/vmstat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/vmstat.c b/mm/vmstat.c index 1284f89fca082a..cdac77398880ce 100644 --- a/mm/vmstat.c +++ b/mm/vmstat.c @@ -1450,7 +1450,7 @@ static void __init start_shepherd_timer(void) int cpu; for_each_possible_cpu(cpu) - INIT_DEFERRABLE_WORK(per_cpu_ptr(&vmstat_work, cpu), + INIT_DELAYED_WORK(per_cpu_ptr(&vmstat_work, cpu), vmstat_update); if (!alloc_cpumask_var(&cpu_stat_off, GFP_KERNEL)) From 5746f2d2f1fad069e2de754310b62e19440c0080 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 9 Feb 2015 11:53:18 +0100 Subject: [PATCH 353/788] sched/autogroup: Fix failure to set cpu.rt_runtime_us commit 1fe89e1b6d270aa0d3452c60d38461ea589594e3 upstream. Because task_group() uses a cache of autogroup_task_group(), whose output depends on sched_class, switching classes can generate problems. In particular, when started as fair, the cache points to the autogroup, so when switching to RT the tg_rt_schedulable() test fails for every cpu.rt_{runtime,period}_us change because now the autogroup has tasks and no runtime. Furthermore, going back to the previous semantics of varying task_group() with sched_class has the down-side that the sched_debug output varies as well, even though the task really is in the autogroup. Therefore add an autogroup exception to tg_has_rt_tasks() -- such that both (all) task_group() usages in sched/core now have one. And remove all the remnants of the variable task_group() output. Reported-by: Zefan Li Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Mike Galbraith Cc: Stefan Bader Fixes: 8323f26ce342 ("sched: Fix race in task_group()") Link: http://lkml.kernel.org/r/20150209112237.GR5029@twins.programming.kicks-ass.net Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- kernel/sched/auto_group.c | 6 +----- kernel/sched/core.c | 6 ++++++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/kernel/sched/auto_group.c b/kernel/sched/auto_group.c index 8a2e230fb86ad4..eae160dd669d9d 100644 --- a/kernel/sched/auto_group.c +++ b/kernel/sched/auto_group.c @@ -87,8 +87,7 @@ static inline struct autogroup *autogroup_create(void) * so we don't have to move tasks around upon policy change, * or flail around trying to allocate bandwidth on the fly. * A bandwidth exception in __sched_setscheduler() allows - * the policy change to proceed. Thereafter, task_group() - * returns &root_task_group, so zero bandwidth is required. + * the policy change to proceed. */ free_rt_sched_group(tg); tg->rt_se = root_task_group.rt_se; @@ -115,9 +114,6 @@ bool task_wants_autogroup(struct task_struct *p, struct task_group *tg) if (tg != &root_task_group) return false; - if (p->sched_class != &fair_sched_class) - return false; - /* * We can only assume the task group can't go away on us if * autogroup_move_group() can see us on ->thread_group list. diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 48c50118b17a59..44dfc8b46bd088 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -7618,6 +7618,12 @@ static inline int tg_has_rt_tasks(struct task_group *tg) { struct task_struct *g, *p; + /* + * Autogroups do not have RT tasks; see autogroup_create(). + */ + if (task_group_is_autogroup(tg)) + return 0; + for_each_process_thread(g, p) { if (rt_task(p) && task_group(p) == tg) return 1; From 40925067a80129d907abbf70dfa5285bbccd1967 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Wed, 24 Dec 2014 17:43:27 +0300 Subject: [PATCH 354/788] clk-gate: fix bit # check in clk_register_gate() commit 2e9dcdae4068460c45a308dd891be5248260251c upstream. In case CLK_GATE_HIWORD_MASK flag is passed to clk_register_gate(), the bit # should be no higher than 15, however the corresponding check is obviously off- by-one. Fixes: 045779942c04 ("clk: gate: add CLK_GATE_HIWORD_MASK") Signed-off-by: Sergei Shtylyov Signed-off-by: Michael Turquette Signed-off-by: Greg Kroah-Hartman --- drivers/clk/clk-gate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index 51fd87fb7ba691..da00eeb95dad6e 100644 --- a/drivers/clk/clk-gate.c +++ b/drivers/clk/clk-gate.c @@ -128,7 +128,7 @@ struct clk *clk_register_gate(struct device *dev, const char *name, struct clk_init_data init; if (clk_gate_flags & CLK_GATE_HIWORD_MASK) { - if (bit_idx > 16) { + if (bit_idx > 15) { pr_err("gate bit exceeds LOWORD field\n"); return ERR_PTR(-EINVAL); } From 6b2ec8b4ab90f059a6db1bd6e9c86fc51512cf0a Mon Sep 17 00:00:00 2001 From: Ryan Grimm Date: Mon, 19 Jan 2015 11:52:48 -0600 Subject: [PATCH 355/788] cxl: Use image state defaults for reloading FPGA commit 4beb5421babee1204757b877622830c6aa31be6d upstream. Select defaults such that a PERST causes flash image reload. Select which image based on what the card is set up to load. CXL_VSEC_PERST_LOADS_IMAGE selects whether PERST assertion causes flash image load. CXL_VSEC_PERST_SELECT_USER selects which image is loaded on the next PERST. cxl_update_image_control writes these bits into the VSEC. Signed-off-by: Ryan Grimm Acked-by: Ian Munsie Signed-off-by: Michael Ellerman Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cxl/cxl.h | 1 + drivers/misc/cxl/pci.c | 42 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/drivers/misc/cxl/cxl.h b/drivers/misc/cxl/cxl.h index 28078f8894a5be..f3856c3eb63b62 100644 --- a/drivers/misc/cxl/cxl.h +++ b/drivers/misc/cxl/cxl.h @@ -481,6 +481,7 @@ void cxl_release_one_irq(struct cxl *adapter, int hwirq); int cxl_alloc_irq_ranges(struct cxl_irq_ranges *irqs, struct cxl *adapter, unsigned int num); void cxl_release_irq_ranges(struct cxl_irq_ranges *irqs, struct cxl *adapter); int cxl_setup_irq(struct cxl *adapter, unsigned int hwirq, unsigned int virq); +int cxl_update_image_control(struct cxl *adapter); /* common == phyp + powernv */ struct cxl_process_element_common { diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c index 0f2cc9f8b4dbcd..184550baa8b9f3 100644 --- a/drivers/misc/cxl/pci.c +++ b/drivers/misc/cxl/pci.c @@ -361,6 +361,41 @@ int cxl_setup_irq(struct cxl *adapter, unsigned int hwirq, return pnv_cxl_ioda_msi_setup(dev, hwirq, virq); } +int cxl_update_image_control(struct cxl *adapter) +{ + struct pci_dev *dev = to_pci_dev(adapter->dev.parent); + int rc; + int vsec; + u8 image_state; + + if (!(vsec = find_cxl_vsec(dev))) { + dev_err(&dev->dev, "ABORTING: CXL VSEC not found!\n"); + return -ENODEV; + } + + if ((rc = CXL_READ_VSEC_IMAGE_STATE(dev, vsec, &image_state))) { + dev_err(&dev->dev, "failed to read image state: %i\n", rc); + return rc; + } + + if (adapter->perst_loads_image) + image_state |= CXL_VSEC_PERST_LOADS_IMAGE; + else + image_state &= ~CXL_VSEC_PERST_LOADS_IMAGE; + + if (adapter->perst_select_user) + image_state |= CXL_VSEC_PERST_SELECT_USER; + else + image_state &= ~CXL_VSEC_PERST_SELECT_USER; + + if ((rc = CXL_WRITE_VSEC_IMAGE_STATE(dev, vsec, image_state))) { + dev_err(&dev->dev, "failed to update image control: %i\n", rc); + return rc; + } + + return 0; +} + int cxl_alloc_one_irq(struct cxl *adapter) { struct pci_dev *dev = to_pci_dev(adapter->dev.parent); @@ -770,8 +805,8 @@ static int cxl_read_vsec(struct cxl *adapter, struct pci_dev *dev) CXL_READ_VSEC_BASE_IMAGE(dev, vsec, &adapter->base_image); CXL_READ_VSEC_IMAGE_STATE(dev, vsec, &image_state); adapter->user_image_loaded = !!(image_state & CXL_VSEC_USER_IMAGE_LOADED); - adapter->perst_loads_image = !!(image_state & CXL_VSEC_PERST_LOADS_IMAGE); - adapter->perst_select_user = !!(image_state & CXL_VSEC_PERST_SELECT_USER); + adapter->perst_loads_image = true; + adapter->perst_select_user = !!(image_state & CXL_VSEC_USER_IMAGE_LOADED); CXL_READ_VSEC_NAFUS(dev, vsec, &adapter->slices); CXL_READ_VSEC_AFU_DESC_OFF(dev, vsec, &afu_desc_off); @@ -879,6 +914,9 @@ static struct cxl *cxl_init_adapter(struct pci_dev *dev) if ((rc = cxl_vsec_looks_ok(adapter, dev))) goto err2; + if ((rc = cxl_update_image_control(adapter))) + goto err2; + if ((rc = cxl_map_adapter_regs(adapter, dev))) goto err2; From d5bc28d181ddc8edf78e4203fae42755a24db94d Mon Sep 17 00:00:00 2001 From: Ryan Grimm Date: Wed, 28 Jan 2015 20:16:04 -0600 Subject: [PATCH 356/788] cxl: Fix device_node reference counting commit 6f963ec2d6bf2476a16799eece920acb2100ff1c upstream. When unbinding and rebinding the driver on a system with a card in PHB0, this error condition is reached after a few attempts: ERROR: Bad of_node_put() on /pciex@3fffe40000000 CPU: 0 PID: 3040 Comm: bash Not tainted 3.18.0-rc3-12545-g3627ffe #152 Call Trace: [c000000721acb5c0] [c00000000086ef94] .dump_stack+0x84/0xb0 (unreliable) [c000000721acb640] [c00000000073a0a8] .of_node_release+0xd8/0xe0 [c000000721acb6d0] [c00000000044bc44] .kobject_release+0x74/0xe0 [c000000721acb760] [c0000000007394fc] .of_node_put+0x1c/0x30 [c000000721acb7d0] [c000000000545cd8] .cxl_probe+0x1a98/0x1d50 [c000000721acb900] [c0000000004845a0] .local_pci_probe+0x40/0xc0 [c000000721acb980] [c000000000484998] .pci_device_probe+0x128/0x170 [c000000721acba30] [c00000000052400c] .driver_probe_device+0xac/0x2a0 [c000000721acbad0] [c000000000522468] .bind_store+0x108/0x160 [c000000721acbb70] [c000000000521448] .drv_attr_store+0x38/0x60 [c000000721acbbe0] [c000000000293840] .sysfs_kf_write+0x60/0xa0 [c000000721acbc50] [c000000000292500] .kernfs_fop_write+0x140/0x1d0 [c000000721acbcf0] [c000000000208648] .vfs_write+0xd8/0x260 [c000000721acbd90] [c000000000208b18] .SyS_write+0x58/0x100 [c000000721acbe30] [c000000000009258] syscall_exit+0x0/0x98 We are missing a call to of_node_get(). pnv_pci_to_phb_node() should call of_node_get() otherwise np's reference count isn't incremented and it might go away. Rename pnv_pci_to_phb_node() to pnv_pci_get_phb_node() so it's clear it calls of_node_get(). Signed-off-by: Ryan Grimm Acked-by: Ian Munsie Signed-off-by: Michael Ellerman Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/include/asm/pnv-pci.h | 2 +- arch/powerpc/platforms/powernv/pci-ioda.c | 6 +++--- drivers/misc/cxl/pci.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/include/asm/pnv-pci.h b/arch/powerpc/include/asm/pnv-pci.h index f09a22fa1bd794..bfa8f8ac51fa49 100644 --- a/arch/powerpc/include/asm/pnv-pci.h +++ b/arch/powerpc/include/asm/pnv-pci.h @@ -19,7 +19,7 @@ int pnv_cxl_ioda_msi_setup(struct pci_dev *dev, unsigned int hwirq, int pnv_cxl_alloc_hwirqs(struct pci_dev *dev, int num); void pnv_cxl_release_hwirqs(struct pci_dev *dev, int hwirq, int num); int pnv_cxl_get_irq_count(struct pci_dev *dev); -struct device_node *pnv_pci_to_phb_node(struct pci_dev *dev); +struct device_node *pnv_pci_get_phb_node(struct pci_dev *dev); #ifdef CONFIG_CXL_BASE int pnv_cxl_alloc_hwirq_ranges(struct cxl_irq_ranges *irqs, diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index fac88ed8a915eb..6a9a255d805800 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -1460,13 +1460,13 @@ static void set_msi_irq_chip(struct pnv_phb *phb, unsigned int virq) #ifdef CONFIG_CXL_BASE -struct device_node *pnv_pci_to_phb_node(struct pci_dev *dev) +struct device_node *pnv_pci_get_phb_node(struct pci_dev *dev) { struct pci_controller *hose = pci_bus_to_host(dev->bus); - return hose->dn; + return of_node_get(hose->dn); } -EXPORT_SYMBOL(pnv_pci_to_phb_node); +EXPORT_SYMBOL(pnv_pci_get_phb_node); int pnv_phb_to_cxl(struct pci_dev *dev) { diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c index 184550baa8b9f3..eee4fd606dc1e4 100644 --- a/drivers/misc/cxl/pci.c +++ b/drivers/misc/cxl/pci.c @@ -316,7 +316,7 @@ static int init_implementation_adapter_regs(struct cxl *adapter, struct pci_dev u64 psl_dsnctl; u64 chipid; - if (!(np = pnv_pci_to_phb_node(dev))) + if (!(np = pnv_pci_get_phb_node(dev))) return -ENODEV; while (np && !(prop = of_get_property(np, "ibm,chip-id", NULL))) From 1181ed21a973f1a8c5a535276685cc09c43a246f Mon Sep 17 00:00:00 2001 From: Ian Munsie Date: Wed, 4 Feb 2015 19:10:38 +1100 Subject: [PATCH 357/788] cxl: Add missing return statement after handling AFU errror commit a6130ed253a931d2169c26ab0958d81b0dce4d6e upstream. We were missing a return statement in the PSL interrupt handler in the case of an AFU error, which would trigger an "Unhandled CXL PSL IRQ" warning. We do actually handle these type of errors (by notifying userspace), so add the missing return IRQ_HANDLED so we don't throw unecessary warnings. Signed-off-by: Ian Munsie Signed-off-by: Michael Ellerman Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cxl/irq.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/misc/cxl/irq.c b/drivers/misc/cxl/irq.c index c294925f73ee4d..bfbe3c8ae7d642 100644 --- a/drivers/misc/cxl/irq.c +++ b/drivers/misc/cxl/irq.c @@ -167,6 +167,7 @@ static irqreturn_t cxl_irq(int irq, void *data, struct cxl_irq_info *irq_info) } cxl_ack_irq(ctx, CXL_PSL_TFC_An_A, 0); + return IRQ_HANDLED; } if (dsisr & CXL_PSL_DSISR_An_OC) pr_devel("CXL interrupt: OS Context Warning\n"); From 4b7326a3f8e9051bd99c3c7241e8565c3c875eea Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 16 Mar 2015 14:52:21 +0100 Subject: [PATCH 358/788] Revert "netfilter: xt_recent: relax ip_pkt_list_tot restrictions" This reverts commit abc86d0f99242b7f142b7cb8f90e30081dd3c256 as it is broken in 3.19 and is easier to revert here than try to fix it. Reported-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org --- net/netfilter/xt_recent.c | 64 +++++++++++---------------------------- 1 file changed, 17 insertions(+), 47 deletions(-) diff --git a/net/netfilter/xt_recent.c b/net/netfilter/xt_recent.c index 30dbe34915ae2b..a9faae89f95533 100644 --- a/net/netfilter/xt_recent.c +++ b/net/netfilter/xt_recent.c @@ -43,29 +43,25 @@ MODULE_LICENSE("GPL"); MODULE_ALIAS("ipt_recent"); MODULE_ALIAS("ip6t_recent"); -static unsigned int ip_list_tot __read_mostly = 100; -static unsigned int ip_list_hash_size __read_mostly; -static unsigned int ip_list_perms __read_mostly = 0644; -static unsigned int ip_list_uid __read_mostly; -static unsigned int ip_list_gid __read_mostly; +static unsigned int ip_list_tot = 100; +static unsigned int ip_pkt_list_tot = 20; +static unsigned int ip_list_hash_size = 0; +static unsigned int ip_list_perms = 0644; +static unsigned int ip_list_uid = 0; +static unsigned int ip_list_gid = 0; module_param(ip_list_tot, uint, 0400); +module_param(ip_pkt_list_tot, uint, 0400); module_param(ip_list_hash_size, uint, 0400); module_param(ip_list_perms, uint, 0400); module_param(ip_list_uid, uint, S_IRUGO | S_IWUSR); module_param(ip_list_gid, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(ip_list_tot, "number of IPs to remember per list"); +MODULE_PARM_DESC(ip_pkt_list_tot, "number of packets per IP address to remember (max. 255)"); MODULE_PARM_DESC(ip_list_hash_size, "size of hash table used to look up IPs"); MODULE_PARM_DESC(ip_list_perms, "permissions on /proc/net/xt_recent/* files"); MODULE_PARM_DESC(ip_list_uid, "default owner of /proc/net/xt_recent/* files"); MODULE_PARM_DESC(ip_list_gid, "default owning group of /proc/net/xt_recent/* files"); -/* retained for backwards compatibility */ -static unsigned int ip_pkt_list_tot __read_mostly; -module_param(ip_pkt_list_tot, uint, 0400); -MODULE_PARM_DESC(ip_pkt_list_tot, "number of packets per IP address to remember (max. 255)"); - -#define XT_RECENT_MAX_NSTAMPS 256 - struct recent_entry { struct list_head list; struct list_head lru_list; @@ -83,7 +79,6 @@ struct recent_table { union nf_inet_addr mask; unsigned int refcnt; unsigned int entries; - u8 nstamps_max_mask; struct list_head lru_list; struct list_head iphash[0]; }; @@ -95,8 +90,7 @@ struct recent_net { #endif }; -static int recent_net_id __read_mostly; - +static int recent_net_id; static inline struct recent_net *recent_pernet(struct net *net) { return net_generic(net, recent_net_id); @@ -177,15 +171,12 @@ recent_entry_init(struct recent_table *t, const union nf_inet_addr *addr, u_int16_t family, u_int8_t ttl) { struct recent_entry *e; - unsigned int nstamps_max = t->nstamps_max_mask; if (t->entries >= ip_list_tot) { e = list_entry(t->lru_list.next, struct recent_entry, lru_list); recent_entry_remove(t, e); } - - nstamps_max += 1; - e = kmalloc(sizeof(*e) + sizeof(e->stamps[0]) * nstamps_max, + e = kmalloc(sizeof(*e) + sizeof(e->stamps[0]) * ip_pkt_list_tot, GFP_ATOMIC); if (e == NULL) return NULL; @@ -206,7 +197,7 @@ recent_entry_init(struct recent_table *t, const union nf_inet_addr *addr, static void recent_entry_update(struct recent_table *t, struct recent_entry *e) { - e->index &= t->nstamps_max_mask; + e->index %= ip_pkt_list_tot; e->stamps[e->index++] = jiffies; if (e->index > e->nstamps) e->nstamps = e->index; @@ -335,7 +326,6 @@ static int recent_mt_check(const struct xt_mtchk_param *par, kuid_t uid; kgid_t gid; #endif - unsigned int nstamp_mask; unsigned int i; int ret = -EINVAL; size_t sz; @@ -359,33 +349,19 @@ static int recent_mt_check(const struct xt_mtchk_param *par, return -EINVAL; if ((info->check_set & XT_RECENT_REAP) && !info->seconds) return -EINVAL; - if (info->hit_count >= XT_RECENT_MAX_NSTAMPS) { - pr_info("hitcount (%u) is larger than allowed maximum (%u)\n", - info->hit_count, XT_RECENT_MAX_NSTAMPS - 1); + if (info->hit_count > ip_pkt_list_tot) { + pr_info("hitcount (%u) is larger than " + "packets to be remembered (%u)\n", + info->hit_count, ip_pkt_list_tot); return -EINVAL; } if (info->name[0] == '\0' || strnlen(info->name, XT_RECENT_NAME_LEN) == XT_RECENT_NAME_LEN) return -EINVAL; - if (ip_pkt_list_tot && info->hit_count < ip_pkt_list_tot) - nstamp_mask = roundup_pow_of_two(ip_pkt_list_tot) - 1; - else if (info->hit_count) - nstamp_mask = roundup_pow_of_two(info->hit_count) - 1; - else - nstamp_mask = 32 - 1; - mutex_lock(&recent_mutex); t = recent_table_lookup(recent_net, info->name); if (t != NULL) { - if (info->hit_count > t->nstamps_max_mask) { - pr_info("hitcount (%u) is larger than packets to be remembered (%u) for table %s\n", - info->hit_count, t->nstamps_max_mask + 1, - info->name); - ret = -EINVAL; - goto out; - } - t->refcnt++; ret = 0; goto out; @@ -401,7 +377,6 @@ static int recent_mt_check(const struct xt_mtchk_param *par, goto out; } t->refcnt = 1; - t->nstamps_max_mask = nstamp_mask; memcpy(&t->mask, &info->mask, sizeof(t->mask)); strcpy(t->name, info->name); @@ -522,12 +497,9 @@ static void recent_seq_stop(struct seq_file *s, void *v) static int recent_seq_show(struct seq_file *seq, void *v) { const struct recent_entry *e = v; - struct recent_iter_state *st = seq->private; - const struct recent_table *t = st->table; unsigned int i; - i = (e->index - 1) & t->nstamps_max_mask; - + i = (e->index - 1) % ip_pkt_list_tot; if (e->family == NFPROTO_IPV4) seq_printf(seq, "src=%pI4 ttl: %u last_seen: %lu oldest_pkt: %u", &e->addr.ip, e->ttl, e->stamps[i], e->index); @@ -745,9 +717,7 @@ static int __init recent_mt_init(void) { int err; - BUILD_BUG_ON_NOT_POWER_OF_2(XT_RECENT_MAX_NSTAMPS); - - if (!ip_list_tot || ip_pkt_list_tot >= XT_RECENT_MAX_NSTAMPS) + if (!ip_list_tot || !ip_pkt_list_tot || ip_pkt_list_tot > 255) return -EINVAL; ip_list_hash_size = 1 << fls(ip_list_tot); From 660613d1a4e94144490850b6c3d350331860fac4 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 18 Mar 2015 14:11:52 +0100 Subject: [PATCH 359/788] Linux 3.19.2 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 688777b1786995..e49665a2b5ac2e 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 19 -SUBLEVEL = 1 +SUBLEVEL = 2 EXTRAVERSION = NAME = Diseased Newt From 915a9ae8258df6a74a2fcb55492536319a184706 Mon Sep 17 00:00:00 2001 From: Rob Gardner Date: Mon, 2 Mar 2015 23:16:55 -0700 Subject: [PATCH 360/788] sparc: semtimedop() unreachable due to comparison error [ Upstream commit 53eb2516972b8c4628651dfcb926cb9ef8b2864a ] A bug was reported that the semtimedop() system call was always failing eith ENOSYS. Since SEMCTL is defined as 3, and SEMTIMEDOP is defined as 4, the comparison "call <= SEMCTL" will always prevent SEMTIMEDOP from getting through to the semaphore ops switch statement. This is corrected by changing the comparison to "call <= SEMTIMEDOP". Orabug: 20633375 Signed-off-by: Rob Gardner Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- arch/sparc/kernel/sys_sparc_64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/sparc/kernel/sys_sparc_64.c b/arch/sparc/kernel/sys_sparc_64.c index c85403d0496c24..30e7ddb27a3a96 100644 --- a/arch/sparc/kernel/sys_sparc_64.c +++ b/arch/sparc/kernel/sys_sparc_64.c @@ -333,7 +333,7 @@ SYSCALL_DEFINE6(sparc_ipc, unsigned int, call, int, first, unsigned long, second long err; /* No need for backward compatibility. We can start fresh... */ - if (call <= SEMCTL) { + if (call <= SEMTIMEDOP) { switch (call) { case SEMOP: err = sys_semtimedop(first, ptr, From 9fd7f980ab9a8910bc31430e45e38a5d1be51147 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Thu, 19 Mar 2015 16:05:57 -0400 Subject: [PATCH 361/788] sparc: perf: Remove redundant perf_pmu_{en|dis}able calls [ Upstream commit 5b0d4b5514bbcce69b516d0742f2cfc84ebd6db3 ] perf_pmu_disable is called by core perf code before pmu->del and the enable function is called by core perf code afterwards. No need to call again within sparc_pmu_del. Ditto for pmu->add and sparc_pmu_add. Signed-off-by: David Ahern Acked-by: Bob Picco Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- arch/sparc/kernel/perf_event.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/sparc/kernel/perf_event.c b/arch/sparc/kernel/perf_event.c index 46a5e450875281..6dc4e793df4cbf 100644 --- a/arch/sparc/kernel/perf_event.c +++ b/arch/sparc/kernel/perf_event.c @@ -1101,7 +1101,6 @@ static void sparc_pmu_del(struct perf_event *event, int _flags) int i; local_irq_save(flags); - perf_pmu_disable(event->pmu); for (i = 0; i < cpuc->n_events; i++) { if (event == cpuc->event[i]) { @@ -1127,7 +1126,6 @@ static void sparc_pmu_del(struct perf_event *event, int _flags) } } - perf_pmu_enable(event->pmu); local_irq_restore(flags); } @@ -1361,7 +1359,6 @@ static int sparc_pmu_add(struct perf_event *event, int ef_flags) unsigned long flags; local_irq_save(flags); - perf_pmu_disable(event->pmu); n0 = cpuc->n_events; if (n0 >= sparc_pmu->max_hw_events) @@ -1394,7 +1391,6 @@ static int sparc_pmu_add(struct perf_event *event, int ef_flags) ret = 0; out: - perf_pmu_enable(event->pmu); local_irq_restore(flags); return ret; } From a65225daef46fa925ffae7625c4fb70bb3203a7b Mon Sep 17 00:00:00 2001 From: David Ahern Date: Thu, 19 Mar 2015 16:06:17 -0400 Subject: [PATCH 362/788] sparc: perf: Make counting mode actually work [ Upstream commit d51291cb8f32bfae6b331e1838651f3ddefa73a5 ] Currently perf-stat (aka, counting mode) does not work: $ perf stat ls ... Performance counter stats for 'ls': 1.585665 task-clock (msec) # 0.580 CPUs utilized 24 context-switches # 0.015 M/sec 0 cpu-migrations # 0.000 K/sec 86 page-faults # 0.054 M/sec cycles stalled-cycles-frontend stalled-cycles-backend instructions branches branch-misses 0.002735100 seconds time elapsed The reason is that state is never reset (stays with PERF_HES_UPTODATE set). Add a call to sparc_pmu_enable_event during the added_event handling. Clean up the encoding since pmu_start calls sparc_pmu_enable_event which does the same. Passing PERF_EF_RELOAD to sparc_pmu_start means the call to sparc_perf_event_set_period can be removed as well. With this patch: $ perf stat ls ... Performance counter stats for 'ls': 1.552890 task-clock (msec) # 0.552 CPUs utilized 24 context-switches # 0.015 M/sec 0 cpu-migrations # 0.000 K/sec 86 page-faults # 0.055 M/sec 5,748,997 cycles # 3.702 GHz stalled-cycles-frontend:HG stalled-cycles-backend:HG 1,684,362 instructions:HG # 0.29 insns per cycle 295,133 branches:HG # 190.054 M/sec 28,007 branch-misses:HG # 9.49% of all branches 0.002815665 seconds time elapsed Signed-off-by: David Ahern Acked-by: Bob Picco Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- arch/sparc/kernel/perf_event.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/arch/sparc/kernel/perf_event.c b/arch/sparc/kernel/perf_event.c index 6dc4e793df4cbf..af53c25da2e7b5 100644 --- a/arch/sparc/kernel/perf_event.c +++ b/arch/sparc/kernel/perf_event.c @@ -960,6 +960,8 @@ static void calculate_single_pcr(struct cpu_hw_events *cpuc) cpuc->pcr[0] |= cpuc->event[0]->hw.config_base; } +static void sparc_pmu_start(struct perf_event *event, int flags); + /* On this PMU each PIC has it's own PCR control register. */ static void calculate_multiple_pcrs(struct cpu_hw_events *cpuc) { @@ -972,20 +974,13 @@ static void calculate_multiple_pcrs(struct cpu_hw_events *cpuc) struct perf_event *cp = cpuc->event[i]; struct hw_perf_event *hwc = &cp->hw; int idx = hwc->idx; - u64 enc; if (cpuc->current_idx[i] != PIC_NO_INDEX) continue; - sparc_perf_event_set_period(cp, hwc, idx); cpuc->current_idx[i] = idx; - enc = perf_event_get_enc(cpuc->events[i]); - cpuc->pcr[idx] &= ~mask_for_index(idx); - if (hwc->state & PERF_HES_STOPPED) - cpuc->pcr[idx] |= nop_for_index(idx); - else - cpuc->pcr[idx] |= event_encoding(enc, idx); + sparc_pmu_start(cp, PERF_EF_RELOAD); } out: for (i = 0; i < cpuc->n_events; i++) { From d19c121b69df3605adaedfb8d6417464ad6e6357 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Thu, 19 Mar 2015 16:06:53 -0400 Subject: [PATCH 363/788] sparc: Touch NMI watchdog when walking cpus and calling printk [ Upstream commit 31aaa98c248da766ece922bbbe8cc78cfd0bc920 ] With the increase in number of CPUs calls to functions that dump output to console (e.g., arch_trigger_all_cpu_backtrace) can take a long time to complete. If IRQs are disabled eventually the NMI watchdog kicks in and creates more havoc. Avoid by telling the NMI watchdog everything is ok. Signed-off-by: David Ahern Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- arch/sparc/kernel/process_64.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/sparc/kernel/process_64.c b/arch/sparc/kernel/process_64.c index 0be7bf978cb1da..46a59643bb1cee 100644 --- a/arch/sparc/kernel/process_64.c +++ b/arch/sparc/kernel/process_64.c @@ -287,6 +287,8 @@ void arch_trigger_all_cpu_backtrace(bool include_self) printk(" TPC[%lx] O7[%lx] I7[%lx] RPC[%lx]\n", gp->tpc, gp->o7, gp->i7, gp->rpc); } + + touch_nmi_watchdog(); } memset(global_cpu_snapshot, 0, sizeof(global_cpu_snapshot)); @@ -362,6 +364,8 @@ static void pmu_snapshot_all_cpus(void) (cpu == this_cpu ? '*' : ' '), cpu, pp->pcr[0], pp->pcr[1], pp->pcr[2], pp->pcr[3], pp->pic[0], pp->pic[1], pp->pic[2], pp->pic[3]); + + touch_nmi_watchdog(); } memset(global_cpu_snapshot, 0, sizeof(global_cpu_snapshot)); From 588caa859398f0d9cc975e74c7e125aac5271e2c Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 23 Mar 2015 09:22:10 -0700 Subject: [PATCH 364/788] sparc64: Fix several bugs in memmove(). [ Upstream commit 2077cef4d5c29cf886192ec32066f783d6a80db8 ] Firstly, handle zero length calls properly. Believe it or not there are a few of these happening during early boot. Next, we can't just drop to a memcpy() call in the forward copy case where dst <= src. The reason is that the cache initializing stores used in the Niagara memcpy() implementations can end up clearing out cache lines before we've sourced their original contents completely. For example, considering NG4memcpy, the main unrolled loop begins like this: load src + 0x00 load src + 0x08 load src + 0x10 load src + 0x18 load src + 0x20 store dst + 0x00 Assume dst is 64 byte aligned and let's say that dst is src - 8 for this memcpy() call. That store at the end there is the one to the first line in the cache line, thus clearing the whole line, which thus clobbers "src + 0x28" before it even gets loaded. To avoid this, just fall through to a simple copy only mildly optimized for the case where src and dst are 8 byte aligned and the length is a multiple of 8 as well. We could get fancy and call GENmemcpy() but this is good enough for how this thing is actually used. Reported-by: David Ahern Reported-by: Bob Picco Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- arch/sparc/lib/memmove.S | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/arch/sparc/lib/memmove.S b/arch/sparc/lib/memmove.S index b7f6334e159f9d..857ad4f8905f94 100644 --- a/arch/sparc/lib/memmove.S +++ b/arch/sparc/lib/memmove.S @@ -8,9 +8,11 @@ .text ENTRY(memmove) /* o0=dst o1=src o2=len */ - mov %o0, %g1 + brz,pn %o2, 99f + mov %o0, %g1 + cmp %o0, %o1 - bleu,pt %xcc, memcpy + bleu,pt %xcc, 2f add %o1, %o2, %g7 cmp %g7, %o0 bleu,pt %xcc, memcpy @@ -24,7 +26,34 @@ ENTRY(memmove) /* o0=dst o1=src o2=len */ stb %g7, [%o0] bne,pt %icc, 1b sub %o0, 1, %o0 - +99: retl mov %g1, %o0 + + /* We can't just call memcpy for these memmove cases. On some + * chips the memcpy uses cache initializing stores and when dst + * and src are close enough, those can clobber the source data + * before we've loaded it in. + */ +2: or %o0, %o1, %g7 + or %o2, %g7, %g7 + andcc %g7, 0x7, %g0 + bne,pn %xcc, 4f + nop + +3: ldx [%o1], %g7 + add %o1, 8, %o1 + subcc %o2, 8, %o2 + add %o0, 8, %o0 + bne,pt %icc, 3b + stx %g7, [%o0 - 0x8] + ba,a,pt %xcc, 99b + +4: ldub [%o1], %g7 + add %o1, 1, %o1 + subcc %o2, 1, %o2 + add %o0, 1, %o0 + bne,pt %icc, 4b + stb %g7, [%o0 - 0x1] + ba,a,pt %xcc, 99b ENDPROC(memmove) From 9f2db938621b313c06e1d90780332a0abf8c2014 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Mon, 9 Mar 2015 17:03:40 -0700 Subject: [PATCH 365/788] net_sched: fix struct tc_u_hnode layout in u32 [ Upstream commit 5778d39d070b4ac5f889928175b7f2d53ae7504e ] We dynamically allocate divisor+1 entries for ->ht[] in tc_u_hnode: ht = kzalloc(sizeof(*ht) + divisor*sizeof(void *), GFP_KERNEL); So ->ht is supposed to be the last field of this struct, however this is broken, since an rcu head is appended after it. Fixes: 1ce87720d456 ("net: sched: make cls_u32 lockless") Cc: Jamal Hadi Salim Cc: John Fastabend Signed-off-by: Cong Wang Acked-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/sched/cls_u32.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index 09487afbfd5187..95fdf4e4005190 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -78,8 +78,11 @@ struct tc_u_hnode { struct tc_u_common *tp_c; int refcnt; unsigned int divisor; - struct tc_u_knode __rcu *ht[1]; struct rcu_head rcu; + /* The 'ht' field MUST be the last field in structure to allow for + * more entries allocated at end of structure. + */ + struct tc_u_knode __rcu *ht[1]; }; struct tc_u_common { From be0e858e52ea730c06486a954b1984e07c9c39f1 Mon Sep 17 00:00:00 2001 From: Nimrod Andy Date: Tue, 10 Mar 2015 19:09:41 +0800 Subject: [PATCH 366/788] net: fec: fix receive VLAN CTAG HW acceleration issue [ Upstream commit af5cbc9822f6bbe399925760a4d5ee82c21f258c ] The current driver support receive VLAN CTAG HW acceleration feature (NETIF_F_HW_VLAN_CTAG_RX) through software simulation. There calls the api .skb_copy_to_linear_data_offset() to skip the VLAN tag, but there have overlap between the two memory data point range. The patch just fix the issue. V2: Michael Grzeschik suggest to use memmove() instead of skb_copy_to_linear_data_offset(). Reported-by: Michael Grzeschik Fixes: 1b7bde6d659d ("net: fec: implement rx_copybreak to improve rx performance") Signed-off-by: Fugang Duan Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/freescale/fec_main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index bba87775419dc9..fd655df63accae 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -1448,8 +1448,7 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) vlan_packet_rcvd = true; - skb_copy_to_linear_data_offset(skb, VLAN_HLEN, - data, (2 * ETH_ALEN)); + memmove(skb->data + VLAN_HLEN, data, ETH_ALEN * 2); skb_pull(skb, VLAN_HLEN); } From 34081d1d2f13fb445194f42790636652525259ba Mon Sep 17 00:00:00 2001 From: Neal Cardwell Date: Tue, 10 Mar 2015 17:17:03 -0400 Subject: [PATCH 367/788] tcp: fix tcp_cong_avoid_ai() credit accumulation bug with decreases in w [ Upstream commit 9949afa42be0b76f5832db112ce51bb6b35b2abb ] The recent change to tcp_cong_avoid_ai() to handle stretch ACKs introduced a bug where snd_cwnd_cnt could accumulate a very large value while w was large, and then if w was reduced snd_cwnd could be incremented by a large delta, leading to a large burst and high packet loss. This was tickled when CUBIC's bictcp_update() sets "ca->cnt = 100 * cwnd". This bug crept in while preparing the upstream version of 814d488c6126. Testing: This patch has been tested in datacenter netperf transfers and live youtube.com and google.com servers. Fixes: 814d488c6126 ("tcp: fix the timid additive increase on stretch ACKs") Signed-off-by: Neal Cardwell Signed-off-by: Yuchung Cheng Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/tcp_cong.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c index 8670e68e2ce67a..f2d40971c71247 100644 --- a/net/ipv4/tcp_cong.c +++ b/net/ipv4/tcp_cong.c @@ -309,6 +309,12 @@ EXPORT_SYMBOL_GPL(tcp_slow_start); */ void tcp_cong_avoid_ai(struct tcp_sock *tp, u32 w, u32 acked) { + /* If credits accumulated at a higher w, apply them gently now. */ + if (tp->snd_cwnd_cnt >= w) { + tp->snd_cwnd_cnt = 0; + tp->snd_cwnd++; + } + tp->snd_cwnd_cnt += acked; if (tp->snd_cwnd_cnt >= w) { u32 delta = tp->snd_cwnd_cnt / w; From 3a48597307784a9063d287177e4452b77a1e1682 Mon Sep 17 00:00:00 2001 From: Neal Cardwell Date: Tue, 10 Mar 2015 17:17:04 -0400 Subject: [PATCH 368/788] tcp: restore 1.5x per RTT limit to CUBIC cwnd growth in congestion avoidance [ Upstream commit d578e18ce93f5d33a7120fd57c453e22a4c0fc37 ] Commit 814d488c6126 ("tcp: fix the timid additive increase on stretch ACKs") fixed a bug where tcp_cong_avoid_ai() would either credit a connection with an increase of snd_cwnd_cnt, or increase snd_cwnd, but not both, resulting in cwnd increasing by 1 packet on at most every alternate invocation of tcp_cong_avoid_ai(). Although the commit correctly implemented the CUBIC algorithm, which can increase cwnd by as much as 1 packet per 1 packet ACKed (2x per RTT), in practice that could be too aggressive: in tests on network paths with small buffers, YouTube server retransmission rates nearly doubled. This commit restores CUBIC to a maximum cwnd growth rate of 1 packet per 2 packets ACKed (1.5x per RTT). In YouTube tests this restored retransmit rates to low levels. Testing: This patch has been tested in datacenter netperf transfers and live youtube.com and google.com servers. Fixes: 9cd981dcf174 ("tcp: fix stretch ACK bugs in CUBIC") Signed-off-by: Neal Cardwell Signed-off-by: Yuchung Cheng Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/tcp_cubic.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c index 4b276d1ed98070..06d3d665a9fd1b 100644 --- a/net/ipv4/tcp_cubic.c +++ b/net/ipv4/tcp_cubic.c @@ -306,8 +306,10 @@ static inline void bictcp_update(struct bictcp *ca, u32 cwnd, u32 acked) } } - if (ca->cnt == 0) /* cannot be zero */ - ca->cnt = 1; + /* The maximum rate of cwnd increase CUBIC allows is 1 packet per + * 2 packets ACKed, meaning cwnd grows at 1.5x per RTT. + */ + ca->cnt = max(ca->cnt, 2U); } static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 acked) From 1e7778e3b37fa5f45aeaf1fb6ba6b1ec3854d0b3 Mon Sep 17 00:00:00 2001 From: Alexey Kodanev Date: Wed, 11 Mar 2015 14:29:17 +0300 Subject: [PATCH 369/788] net: sysctl_net_core: check SNDBUF and RCVBUF for min length [ Upstream commit b1cb59cf2efe7971d3d72a7b963d09a512d994c9 ] sysctl has sysctl.net.core.rmem_*/wmem_* parameters which can be set to incorrect values. Given that 'struct sk_buff' allocates from rcvbuf, incorrectly set buffer length could result to memory allocation failures. For example, set them as follows: # sysctl net.core.rmem_default=64 net.core.wmem_default = 64 # sysctl net.core.wmem_default=64 net.core.wmem_default = 64 # ping localhost -s 1024 -i 0 > /dev/null This could result to the following failure: skbuff: skb_over_panic: text:ffffffff81628db4 len:-32 put:-32 head:ffff88003a1cc200 data:ffff88003a1cc200 tail:0xffffffe0 end:0xc0 dev: kernel BUG at net/core/skbuff.c:102! invalid opcode: 0000 [#1] SMP ... task: ffff88003b7f5550 ti: ffff88003ae88000 task.ti: ffff88003ae88000 RIP: 0010:[] [] skb_put+0xa1/0xb0 RSP: 0018:ffff88003ae8bc68 EFLAGS: 00010296 RAX: 000000000000008d RBX: 00000000ffffffe0 RCX: 0000000000000000 RDX: ffff88003fdcf598 RSI: ffff88003fdcd9c8 RDI: ffff88003fdcd9c8 RBP: ffff88003ae8bc88 R08: 0000000000000001 R09: 0000000000000000 R10: 0000000000000001 R11: 00000000000002b2 R12: 0000000000000000 R13: 0000000000000000 R14: ffff88003d3f7300 R15: ffff88000012a900 FS: 00007fa0e2b4a840(0000) GS:ffff88003fc00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000d0f7e0 CR3: 000000003b8fb000 CR4: 00000000000006f0 Stack: ffff88003a1cc200 00000000ffffffe0 00000000000000c0 ffffffff818cab1d ffff88003ae8bd68 ffffffff81628db4 ffff88003ae8bd48 ffff88003b7f5550 ffff880031a09408 ffff88003b7f5550 ffff88000012aa48 ffff88000012ab00 Call Trace: [] unix_stream_sendmsg+0x2c4/0x470 [] sock_write_iter+0x146/0x160 [] new_sync_write+0x92/0xd0 [] vfs_write+0xd6/0x180 [] SyS_write+0x59/0xd0 [] system_call_fastpath+0x12/0x17 Code: 00 00 48 89 44 24 10 8b 87 c8 00 00 00 48 89 44 24 08 48 8b 87 d8 00 00 00 48 c7 c7 30 db 91 81 48 89 04 24 31 c0 e8 4f a8 0e 00 <0f> 0b eb fe 66 66 2e 0f 1f 84 00 00 00 00 00 55 48 89 e5 48 83 RIP [] skb_put+0xa1/0xb0 RSP Kernel panic - not syncing: Fatal exception Moreover, the possible minimum is 1, so we can get another kernel panic: ... BUG: unable to handle kernel paging request at ffff88013caee5c0 IP: [] __alloc_skb+0x12f/0x1f0 ... Signed-off-by: Alexey Kodanev Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/core/sysctl_net_core.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index 31baba2a71ce15..bbb1d5ac4a721a 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -25,6 +25,8 @@ static int zero = 0; static int one = 1; static int ushort_max = USHRT_MAX; +static int min_sndbuf = SOCK_MIN_SNDBUF; +static int min_rcvbuf = SOCK_MIN_RCVBUF; static int net_msg_warn; /* Unused, but still a sysctl */ @@ -237,7 +239,7 @@ static struct ctl_table net_core_table[] = { .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec_minmax, - .extra1 = &one, + .extra1 = &min_sndbuf, }, { .procname = "rmem_max", @@ -245,7 +247,7 @@ static struct ctl_table net_core_table[] = { .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec_minmax, - .extra1 = &one, + .extra1 = &min_rcvbuf, }, { .procname = "wmem_default", @@ -253,7 +255,7 @@ static struct ctl_table net_core_table[] = { .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec_minmax, - .extra1 = &one, + .extra1 = &min_sndbuf, }, { .procname = "rmem_default", @@ -261,7 +263,7 @@ static struct ctl_table net_core_table[] = { .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec_minmax, - .extra1 = &one, + .extra1 = &min_rcvbuf, }, { .procname = "dev_weight", From 3bde7bbefb539e516f311a0efc47dda08247d1a7 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 11 Mar 2015 22:46:59 +0100 Subject: [PATCH 370/788] rds: avoid potential stack overflow [ Upstream commit f862e07cf95d5b62a5fc5e981dd7d0dbaf33a501 ] The rds_iw_update_cm_id function stores a large 'struct rds_sock' object on the stack in order to pass a pair of addresses. This happens to just fit withint the 1024 byte stack size warning limit on x86, but just exceed that limit on ARM, which gives us this warning: net/rds/iw_rdma.c:200:1: warning: the frame size of 1056 bytes is larger than 1024 bytes [-Wframe-larger-than=] As the use of this large variable is basically bogus, we can rearrange the code to not do that. Instead of passing an rds socket into rds_iw_get_device, we now just pass the two addresses that we have available in rds_iw_update_cm_id, and we change rds_iw_get_mr accordingly, to create two address structures on the stack there. Signed-off-by: Arnd Bergmann Acked-by: Sowmini Varadhan Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/rds/iw_rdma.c | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/net/rds/iw_rdma.c b/net/rds/iw_rdma.c index a817705ce2d0e9..dba8d0864f1804 100644 --- a/net/rds/iw_rdma.c +++ b/net/rds/iw_rdma.c @@ -88,7 +88,9 @@ static unsigned int rds_iw_unmap_fastreg_list(struct rds_iw_mr_pool *pool, int *unpinned); static void rds_iw_destroy_fastreg(struct rds_iw_mr_pool *pool, struct rds_iw_mr *ibmr); -static int rds_iw_get_device(struct rds_sock *rs, struct rds_iw_device **rds_iwdev, struct rdma_cm_id **cm_id) +static int rds_iw_get_device(struct sockaddr_in *src, struct sockaddr_in *dst, + struct rds_iw_device **rds_iwdev, + struct rdma_cm_id **cm_id) { struct rds_iw_device *iwdev; struct rds_iw_cm_id *i_cm_id; @@ -112,15 +114,15 @@ static int rds_iw_get_device(struct rds_sock *rs, struct rds_iw_device **rds_iwd src_addr->sin_port, dst_addr->sin_addr.s_addr, dst_addr->sin_port, - rs->rs_bound_addr, - rs->rs_bound_port, - rs->rs_conn_addr, - rs->rs_conn_port); + src->sin_addr.s_addr, + src->sin_port, + dst->sin_addr.s_addr, + dst->sin_port); #ifdef WORKING_TUPLE_DETECTION - if (src_addr->sin_addr.s_addr == rs->rs_bound_addr && - src_addr->sin_port == rs->rs_bound_port && - dst_addr->sin_addr.s_addr == rs->rs_conn_addr && - dst_addr->sin_port == rs->rs_conn_port) { + if (src_addr->sin_addr.s_addr == src->sin_addr.s_addr && + src_addr->sin_port == src->sin_port && + dst_addr->sin_addr.s_addr == dst->sin_addr.s_addr && + dst_addr->sin_port == dst->sin_port) { #else /* FIXME - needs to compare the local and remote * ipaddr/port tuple, but the ipaddr is the only @@ -128,7 +130,7 @@ static int rds_iw_get_device(struct rds_sock *rs, struct rds_iw_device **rds_iwd * zero'ed. It doesn't appear to be properly populated * during connection setup... */ - if (src_addr->sin_addr.s_addr == rs->rs_bound_addr) { + if (src_addr->sin_addr.s_addr == src->sin_addr.s_addr) { #endif spin_unlock_irq(&iwdev->spinlock); *rds_iwdev = iwdev; @@ -180,19 +182,13 @@ int rds_iw_update_cm_id(struct rds_iw_device *rds_iwdev, struct rdma_cm_id *cm_i { struct sockaddr_in *src_addr, *dst_addr; struct rds_iw_device *rds_iwdev_old; - struct rds_sock rs; struct rdma_cm_id *pcm_id; int rc; src_addr = (struct sockaddr_in *)&cm_id->route.addr.src_addr; dst_addr = (struct sockaddr_in *)&cm_id->route.addr.dst_addr; - rs.rs_bound_addr = src_addr->sin_addr.s_addr; - rs.rs_bound_port = src_addr->sin_port; - rs.rs_conn_addr = dst_addr->sin_addr.s_addr; - rs.rs_conn_port = dst_addr->sin_port; - - rc = rds_iw_get_device(&rs, &rds_iwdev_old, &pcm_id); + rc = rds_iw_get_device(src_addr, dst_addr, &rds_iwdev_old, &pcm_id); if (rc) rds_iw_remove_cm_id(rds_iwdev, cm_id); @@ -598,9 +594,17 @@ void *rds_iw_get_mr(struct scatterlist *sg, unsigned long nents, struct rds_iw_device *rds_iwdev; struct rds_iw_mr *ibmr = NULL; struct rdma_cm_id *cm_id; + struct sockaddr_in src = { + .sin_addr.s_addr = rs->rs_bound_addr, + .sin_port = rs->rs_bound_port, + }; + struct sockaddr_in dst = { + .sin_addr.s_addr = rs->rs_conn_addr, + .sin_port = rs->rs_conn_port, + }; int ret; - ret = rds_iw_get_device(rs, &rds_iwdev, &cm_id); + ret = rds_iw_get_device(&src, &dst, &rds_iwdev, &cm_id); if (ret || !cm_id) { ret = -ENODEV; goto out; From cdffd074bb2d55d7acbd8b6ba73a6e04ce2b080d Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Thu, 12 Mar 2015 13:57:44 +0800 Subject: [PATCH 371/788] virtio-net: correctly delete napi hash [ Upstream commit ab3971b1e7d72270a2a259a29c1a40351b889740 ] We don't delete napi from hash list during module exit. This will cause the following panic when doing module load and unload: BUG: unable to handle kernel paging request at 0000004e00000075 IP: [] napi_hash_add+0x6b/0xf0 PGD 3c5d5067 PUD 0 Oops: 0000 [#1] SMP ... Call Trace: [] init_vqs+0x107/0x490 [virtio_net] [] virtnet_probe+0x562/0x791815639d880be [virtio_net] [] virtio_dev_probe+0x137/0x200 [] driver_probe_device+0x7a/0x250 [] __driver_attach+0x93/0xa0 [] ? __device_attach+0x40/0x40 [] bus_for_each_dev+0x63/0xa0 [] driver_attach+0x19/0x20 [] bus_add_driver+0x170/0x220 [] ? 0xffffffffa0a60000 [] driver_register+0x5f/0xf0 [] register_virtio_driver+0x1b/0x30 [] virtio_net_driver_init+0x10/0x12 [virtio_net] This patch fixes this by doing this in virtnet_free_queues(). And also don't delete napi in virtnet_freeze() since it will call virtnet_free_queues() which has already did this. Fixes 91815639d880 ("virtio-net: rx busy polling support") Cc: Rusty Russell Cc: Michael S. Tsirkin Signed-off-by: Jason Wang Acked-by: Michael S. Tsirkin Reviewed-by: Michael S. Tsirkin Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/virtio_net.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 059fdf1bf5eed9..0ad6c0c1b00d18 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -1444,8 +1444,10 @@ static void virtnet_free_queues(struct virtnet_info *vi) { int i; - for (i = 0; i < vi->max_queue_pairs; i++) + for (i = 0; i < vi->max_queue_pairs; i++) { + napi_hash_del(&vi->rq[i].napi); netif_napi_del(&vi->rq[i].napi); + } kfree(vi->rq); kfree(vi->sq); @@ -1936,11 +1938,8 @@ static int virtnet_freeze(struct virtio_device *vdev) cancel_delayed_work_sync(&vi->refill); if (netif_running(vi->dev)) { - for (i = 0; i < vi->max_queue_pairs; i++) { + for (i = 0; i < vi->max_queue_pairs; i++) napi_disable(&vi->rq[i].napi); - napi_hash_del(&vi->rq[i].napi); - netif_napi_del(&vi->rq[i].napi); - } } remove_vq_common(vi); From 569043563308fd7c9dbf0d93c71c60cab57a87f0 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 13 Mar 2015 09:49:59 -0700 Subject: [PATCH 372/788] inet_diag: fix possible overflow in inet_diag_dump_one_icsk() [ Upstream commit c8e2c80d7ec00d020320f905822bf49c5ad85250 ] inet_diag_dump_one_icsk() allocates too small skb. Add inet_sk_attr_size() helper right before inet_sk_diag_fill() so that it can be updated if/when new attributes are added. iproute2/ss currently does not use this dump_one() interface, this might explain nobody noticed this problem yet. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/inet_diag.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index e34dccbc4d70bd..4eeba4e497a02b 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -71,6 +71,20 @@ static inline void inet_diag_unlock_handler( mutex_unlock(&inet_diag_table_mutex); } +static size_t inet_sk_attr_size(void) +{ + return nla_total_size(sizeof(struct tcp_info)) + + nla_total_size(1) /* INET_DIAG_SHUTDOWN */ + + nla_total_size(1) /* INET_DIAG_TOS */ + + nla_total_size(1) /* INET_DIAG_TCLASS */ + + nla_total_size(sizeof(struct inet_diag_meminfo)) + + nla_total_size(sizeof(struct inet_diag_msg)) + + nla_total_size(SK_MEMINFO_VARS * sizeof(u32)) + + nla_total_size(TCP_CA_NAME_MAX) + + nla_total_size(sizeof(struct tcpvegas_info)) + + 64; +} + int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, struct sk_buff *skb, struct inet_diag_req_v2 *req, struct user_namespace *user_ns, @@ -324,9 +338,7 @@ int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *in_s if (err) goto out; - rep = nlmsg_new(sizeof(struct inet_diag_msg) + - sizeof(struct inet_diag_meminfo) + - sizeof(struct tcp_info) + 64, GFP_KERNEL); + rep = nlmsg_new(inet_sk_attr_size(), GFP_KERNEL); if (!rep) { err = -ENOMEM; goto out; From 031348c34164dc8eb4c61f7db569c27333d2a71d Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 14 Mar 2015 05:22:21 +0000 Subject: [PATCH 373/788] caif: fix MSG_OOB test in caif_seqpkt_recvmsg() [ Upstream commit 3eeff778e00c956875c70b145c52638c313dfb23 ] It should be checking flags, not msg->msg_flags. It's ->sendmsg() instances that need to look for that in ->msg_flags, ->recvmsg() ones (including the other ->recvmsg() instance in that file, as well as unix_dgram_recvmsg() this one claims to be imitating) check in flags. Braino had been introduced in commit dcda13 ("caif: Bugfix - use MSG_TRUNC in receive") back in 2010, so it goes quite a while back. Signed-off-by: Al Viro Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/caif/caif_socket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c index 769b185fefbd5f..a6e2da0bc71845 100644 --- a/net/caif/caif_socket.c +++ b/net/caif/caif_socket.c @@ -281,7 +281,7 @@ static int caif_seqpkt_recvmsg(struct kiocb *iocb, struct socket *sock, int copylen; ret = -EOPNOTSUPP; - if (m->msg_flags&MSG_OOB) + if (flags & MSG_OOB) goto read_error; skb = skb_recv_datagram(sk, flags, 0 , &ret); From 3bff11ddef64820ea8920e850ef0681734527c27 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 14 Mar 2015 05:34:56 +0000 Subject: [PATCH 374/788] rxrpc: bogus MSG_PEEK test in rxrpc_recvmsg() [ Upstream commit 7d985ed1dca5c90535d67ce92ef6ca520302340a ] [I would really like an ACK on that one from dhowells; it appears to be quite straightforward, but...] MSG_PEEK isn't passed to ->recvmsg() via msg->msg_flags; as the matter of fact, neither the kernel users of rxrpc, nor the syscalls ever set that bit in there. It gets passed via flags; in fact, another such check in the same function is done correctly - as flags & MSG_PEEK. It had been that way (effectively disabled) for 8 years, though, so the patch needs beating up - that case had never been tested. If it is correct, it's -stable fodder. Signed-off-by: Al Viro Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/rxrpc/ar-recvmsg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/rxrpc/ar-recvmsg.c b/net/rxrpc/ar-recvmsg.c index 4575485ad1b4d0..19a560626dc4f4 100644 --- a/net/rxrpc/ar-recvmsg.c +++ b/net/rxrpc/ar-recvmsg.c @@ -87,7 +87,7 @@ int rxrpc_recvmsg(struct kiocb *iocb, struct socket *sock, if (!skb) { /* nothing remains on the queue */ if (copied && - (msg->msg_flags & MSG_PEEK || timeo == 0)) + (flags & MSG_PEEK || timeo == 0)) goto out; /* wait for a message to turn up */ From 7e1df1a11cc1cc993b924400e46d48085eb281b2 Mon Sep 17 00:00:00 2001 From: Eran Ben Elisha Date: Wed, 18 Mar 2015 16:51:36 +0200 Subject: [PATCH 375/788] net/mlx4_en: Fix off-by-one in ethtool statistics display [ Upstream commit a16f3565703cfc3094938fb3c979cbb90f6d9eb4 ] NUM_PORT_STATS was 9 instead of 10, which caused off-by-one bug when displaying the statistics starting from tx_chksum_offload in ethtool. Fixes: f8c6455bb04b ('net/mlx4_en: Extend checksum offloading by CHECKSUM COMPLETE') Signed-off-by: Eran Ben Elisha Signed-off-by: Hadar Hen Zion Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 944a112dff374e..8805ef1a4c7506 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -451,7 +451,7 @@ struct mlx4_en_port_stats { unsigned long rx_chksum_none; unsigned long rx_chksum_complete; unsigned long tx_chksum_offload; -#define NUM_PORT_STATS 9 +#define NUM_PORT_STATS 10 }; struct mlx4_en_perf_stats { From 2604c9c02808f95da9b7c0cf6b03f381b3c4483c Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Wed, 18 Mar 2015 23:01:01 +0100 Subject: [PATCH 376/788] Revert "net: cx82310_eth: use common match macro" [ Upstream commit 8d006e0105978619fb472e150c88b0d49337fe2b ] This reverts commit 11ad714b98f6d9ca0067568442afe3e70eb94845 because it breaks cx82310_eth. The custom USB_DEVICE_CLASS macro matches bDeviceClass, bDeviceSubClass and bDeviceProtocol but the common USB_DEVICE_AND_INTERFACE_INFO matches bInterfaceClass, bInterfaceSubClass and bInterfaceProtocol instead, which are not specified. Signed-off-by: Ondrej Zary Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/usb/cx82310_eth.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/net/usb/cx82310_eth.c b/drivers/net/usb/cx82310_eth.c index 3eed708a6182e7..fe48f4c513730f 100644 --- a/drivers/net/usb/cx82310_eth.c +++ b/drivers/net/usb/cx82310_eth.c @@ -300,9 +300,18 @@ static const struct driver_info cx82310_info = { .tx_fixup = cx82310_tx_fixup, }; +#define USB_DEVICE_CLASS(vend, prod, cl, sc, pr) \ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ + USB_DEVICE_ID_MATCH_DEV_INFO, \ + .idVendor = (vend), \ + .idProduct = (prod), \ + .bDeviceClass = (cl), \ + .bDeviceSubClass = (sc), \ + .bDeviceProtocol = (pr) + static const struct usb_device_id products[] = { { - USB_DEVICE_AND_INTERFACE_INFO(0x0572, 0xcb01, 0xff, 0, 0), + USB_DEVICE_CLASS(0x0572, 0xcb01, 0xff, 0, 0), .driver_info = (unsigned long) &cx82310_info }, { }, From a0e0c4addb570f6811ade2911f3cc71ee86d9251 Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Thu, 19 Mar 2015 11:22:32 +0100 Subject: [PATCH 377/788] ipv6: call ipv6_proxy_select_ident instead of ipv6_select_ident in udp6_ufo_fragment [ Upstream commit 8e199dfd82ee097b522b00344af6448715d8ee0c ] Matt Grant reported frequent crashes in ipv6_select_ident when udp6_ufo_fragment is called from openvswitch on a skb that doesn't have a dst_entry set. ipv6_proxy_select_ident generates the frag_id without using the dst associated with the skb. This approach was suggested by Vladislav Yasevich. Fixes: 0508c07f5e0c ("ipv6: Select fragment id during UFO segmentation if not set.") Cc: Vladislav Yasevich Reported-by: Matt Grant Tested-by: Matt Grant Signed-off-by: Sabrina Dubroca Acked-by: Vladislav Yasevich Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv6/udp_offload.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c index a56276996b72b3..4b869d324010e3 100644 --- a/net/ipv6/udp_offload.c +++ b/net/ipv6/udp_offload.c @@ -112,11 +112,9 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen); fptr->nexthdr = nexthdr; fptr->reserved = 0; - if (skb_shinfo(skb)->ip6_frag_id) - fptr->identification = skb_shinfo(skb)->ip6_frag_id; - else - ipv6_select_ident(fptr, - (struct rt6_info *)skb_dst(skb)); + if (!skb_shinfo(skb)->ip6_frag_id) + ipv6_proxy_select_ident(skb); + fptr->identification = skb_shinfo(skb)->ip6_frag_id; /* Fragment the skb. ipv6 header and the remaining fields of the * fragment header are updated in ipv6_gso_segment() From 8f1e9a5da7b864ee71a230e9a0d590c65c05079b Mon Sep 17 00:00:00 2001 From: Steven Barth Date: Thu, 19 Mar 2015 16:16:04 +0100 Subject: [PATCH 378/788] ipv6: fix backtracking for throw routes [ Upstream commit 73ba57bfae4a1914f6a6dac71e3168dd900e00af ] for throw routes to trigger evaluation of other policy rules EAGAIN needs to be propagated up to fib_rules_lookup similar to how its done for IPv4 A simple testcase for verification is: ip -6 rule add lookup 33333 priority 33333 ip -6 route add throw 2001:db8::1 ip -6 route add 2001:db8::1 via fe80::1 dev wlan0 table 33333 ip route get 2001:db8::1 Signed-off-by: Steven Barth Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv6/fib6_rules.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c index b4d5e1d97c1b25..27ca79682efbf6 100644 --- a/net/ipv6/fib6_rules.c +++ b/net/ipv6/fib6_rules.c @@ -104,6 +104,7 @@ static int fib6_rule_action(struct fib_rule *rule, struct flowi *flp, goto again; flp6->saddr = saddr; } + err = rt->dst.error; goto out; } again: From 179d478522bf66ad33b6b075066a27f6b0945fcd Mon Sep 17 00:00:00 2001 From: Josh Hunt Date: Thu, 19 Mar 2015 19:19:30 -0400 Subject: [PATCH 379/788] tcp: fix tcp fin memory accounting [ Upstream commit d22e1537181188e5dc8cbc51451832625035bdc2 ] tcp_send_fin() does not account for the memory it allocates properly, so sk_forward_alloc can be negative in cases where we've sent a FIN: ss example output (ss -amn | grep -B1 f4294): tcp FIN-WAIT-1 0 1 192.168.0.1:45520 192.0.2.1:8080 skmem:(r0,rb87380,t0,tb87380,f4294966016,w1280,o0,bl0) Acked-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/tcp_output.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 65caf8b95e1722..9790f396ce5ee8 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2775,15 +2775,11 @@ void tcp_send_fin(struct sock *sk) } else { /* Socket is locked, keep trying until memory is available. */ for (;;) { - skb = alloc_skb_fclone(MAX_TCP_HEADER, - sk->sk_allocation); + skb = sk_stream_alloc_skb(sk, 0, sk->sk_allocation); if (skb) break; yield(); } - - /* Reserve space for headers and prepare control bits. */ - skb_reserve(skb, MAX_TCP_HEADER); /* FIN eats a sequence byte, write_seq advanced by tcp_queue_skb(). */ tcp_init_nondata_skb(skb, tp->write_seq, TCPHDR_ACK | TCPHDR_FIN); From 554b7d2eeeb4daa535fafb65e10305899617cd3e Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Fri, 20 Mar 2015 16:48:13 +0000 Subject: [PATCH 380/788] net: compat: Update get_compat_msghdr() to match copy_msghdr_from_user() behaviour [ Upstream commit 91edd096e224941131f896b86838b1e59553696a ] Commit db31c55a6fb2 (net: clamp ->msg_namelen instead of returning an error) introduced the clamping of msg_namelen when the unsigned value was larger than sizeof(struct sockaddr_storage). This caused a msg_namelen of -1 to be valid. The native code was subsequently fixed by commit dbb490b96584 (net: socket: error on a negative msg_namelen). In addition, the native code sets msg_namelen to 0 when msg_name is NULL. This was done in commit (6a2a2b3ae075 net:socket: set msg_namelen to 0 if msg_name is passed as NULL in msghdr struct from userland) and subsequently updated by 08adb7dabd48 (fold verify_iovec() into copy_msghdr_from_user()). This patch brings the get_compat_msghdr() in line with copy_msghdr_from_user(). Fixes: db31c55a6fb2 (net: clamp ->msg_namelen instead of returning an error) Cc: David S. Miller Cc: Dan Carpenter Signed-off-by: Catalin Marinas Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/compat.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/net/compat.c b/net/compat.c index 94d3d5e978832c..f7bd286a828071 100644 --- a/net/compat.c +++ b/net/compat.c @@ -49,6 +49,13 @@ ssize_t get_compat_msghdr(struct msghdr *kmsg, __get_user(kmsg->msg_controllen, &umsg->msg_controllen) || __get_user(kmsg->msg_flags, &umsg->msg_flags)) return -EFAULT; + + if (!uaddr) + kmsg->msg_namelen = 0; + + if (kmsg->msg_namelen < 0) + return -EINVAL; + if (kmsg->msg_namelen > sizeof(struct sockaddr_storage)) kmsg->msg_namelen = sizeof(struct sockaddr_storage); kmsg->msg_control = compat_ptr(tmp3); From a39960b04c21381bab7fca79c6461f35074128f5 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 20 Mar 2015 17:41:43 +0000 Subject: [PATCH 381/788] net: validate the range we feed to iov_iter_init() in sys_sendto/sys_recvfrom commit 4de930efc23b92ddf88ce91c405ee645fe6e27ea upstream. Signed-off-by: Al Viro Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/socket.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/socket.c b/net/socket.c index 418795caa8979f..d50e7ca6aeeadb 100644 --- a/net/socket.c +++ b/net/socket.c @@ -1765,6 +1765,8 @@ SYSCALL_DEFINE6(sendto, int, fd, void __user *, buff, size_t, len, if (len > INT_MAX) len = INT_MAX; + if (unlikely(!access_ok(VERIFY_READ, buff, len))) + return -EFAULT; sock = sockfd_lookup_light(fd, &err, &fput_needed); if (!sock) goto out; @@ -1823,6 +1825,8 @@ SYSCALL_DEFINE6(recvfrom, int, fd, void __user *, ubuf, size_t, size, if (size > INT_MAX) size = INT_MAX; + if (unlikely(!access_ok(VERIFY_WRITE, ubuf, size))) + return -EFAULT; sock = sockfd_lookup_light(fd, &err, &fput_needed); if (!sock) goto out; From 2195ac7f1315c7aa7e2d36c7dcab95f5ed1387ef Mon Sep 17 00:00:00 2001 From: "Suzuki K. Poulose" Date: Thu, 19 Mar 2015 18:17:09 +0000 Subject: [PATCH 382/788] arm64: Honor __GFP_ZERO in dma allocations commit 7132813c384515c9dede1ae20e56f3895feb7f1e upstream. Current implementation doesn't zero out the pages allocated. Honor the __GFP_ZERO flag and zero out if set. Acked-by: Will Deacon Signed-off-by: Suzuki K. Poulose Signed-off-by: Catalin Marinas Signed-off-by: Greg Kroah-Hartman --- arch/arm64/mm/dma-mapping.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c index d9209420391381..df34a70caca199 100644 --- a/arch/arm64/mm/dma-mapping.c +++ b/arch/arm64/mm/dma-mapping.c @@ -51,7 +51,7 @@ static int __init early_coherent_pool(char *p) } early_param("coherent_pool", early_coherent_pool); -static void *__alloc_from_pool(size_t size, struct page **ret_page) +static void *__alloc_from_pool(size_t size, struct page **ret_page, gfp_t flags) { unsigned long val; void *ptr = NULL; @@ -67,6 +67,8 @@ static void *__alloc_from_pool(size_t size, struct page **ret_page) *ret_page = phys_to_page(phys); ptr = (void *)val; + if (flags & __GFP_ZERO) + memset(ptr, 0, size); } return ptr; @@ -101,6 +103,7 @@ static void *__dma_alloc_coherent(struct device *dev, size_t size, flags |= GFP_DMA; if (IS_ENABLED(CONFIG_DMA_CMA) && (flags & __GFP_WAIT)) { struct page *page; + void *addr; size = PAGE_ALIGN(size); page = dma_alloc_from_contiguous(dev, size >> PAGE_SHIFT, @@ -109,7 +112,10 @@ static void *__dma_alloc_coherent(struct device *dev, size_t size, return NULL; *dma_handle = phys_to_dma(dev, page_to_phys(page)); - return page_address(page); + addr = page_address(page); + if (flags & __GFP_ZERO) + memset(addr, 0, size); + return addr; } else { return swiotlb_alloc_coherent(dev, size, dma_handle, flags); } @@ -145,7 +151,7 @@ static void *__dma_alloc_noncoherent(struct device *dev, size_t size, if (!(flags & __GFP_WAIT)) { struct page *page = NULL; - void *addr = __alloc_from_pool(size, &page); + void *addr = __alloc_from_pool(size, &page, flags); if (addr) *dma_handle = phys_to_dma(dev, page_to_phys(page)); From 79b99717f6046c0d7ea4b3b41481bf89548fca6e Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Wed, 11 Mar 2015 12:20:39 +0000 Subject: [PATCH 383/788] arm64: Invalidate the TLB corresponding to intermediate page table levels commit 285994a62c80f1d72c6924282bcb59608098d5ec upstream. The ARM architecture allows the caching of intermediate page table levels and page table freeing requires a sequence like: pmd_clear() TLB invalidation pte page freeing With commit 5e5f6dc10546 (arm64: mm: enable HAVE_RCU_TABLE_FREE logic), the page table freeing batching was moved from tlb_remove_page() to tlb_remove_table(). The former takes care of TLB invalidation as this is also shared with pte clearing and page cache page freeing. The latter, however, does not invalidate the TLBs for intermediate page table levels as it probably relies on the architecture code to do it if required. When the mm->mm_users < 2, tlb_remove_table() does not do any batching and page table pages are freed before tlb_finish_mmu() which performs the actual TLB invalidation. This patch introduces __tlb_flush_pgtable() for arm64 and calls it from the {pte,pmd,pud}_free_tlb() directly without relying on deferred page table freeing. Fixes: 5e5f6dc10546 arm64: mm: enable HAVE_RCU_TABLE_FREE logic Reported-by: Jon Masters Tested-by: Jon Masters Tested-by: Steve Capper Signed-off-by: Catalin Marinas Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/tlb.h | 3 +++ arch/arm64/include/asm/tlbflush.h | 13 +++++++++++++ 2 files changed, 16 insertions(+) diff --git a/arch/arm64/include/asm/tlb.h b/arch/arm64/include/asm/tlb.h index c028fe37456fea..53d9c354219f97 100644 --- a/arch/arm64/include/asm/tlb.h +++ b/arch/arm64/include/asm/tlb.h @@ -48,6 +48,7 @@ static inline void tlb_flush(struct mmu_gather *tlb) static inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte, unsigned long addr) { + __flush_tlb_pgtable(tlb->mm, addr); pgtable_page_dtor(pte); tlb_remove_entry(tlb, pte); } @@ -56,6 +57,7 @@ static inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte, static inline void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmdp, unsigned long addr) { + __flush_tlb_pgtable(tlb->mm, addr); tlb_remove_entry(tlb, virt_to_page(pmdp)); } #endif @@ -64,6 +66,7 @@ static inline void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmdp, static inline void __pud_free_tlb(struct mmu_gather *tlb, pud_t *pudp, unsigned long addr) { + __flush_tlb_pgtable(tlb->mm, addr); tlb_remove_entry(tlb, virt_to_page(pudp)); } #endif diff --git a/arch/arm64/include/asm/tlbflush.h b/arch/arm64/include/asm/tlbflush.h index 73f0ce570fb31c..8b8d8cb46e018d 100644 --- a/arch/arm64/include/asm/tlbflush.h +++ b/arch/arm64/include/asm/tlbflush.h @@ -148,6 +148,19 @@ static inline void flush_tlb_kernel_range(unsigned long start, unsigned long end flush_tlb_all(); } +/* + * Used to invalidate the TLB (walk caches) corresponding to intermediate page + * table levels (pgd/pud/pmd). + */ +static inline void __flush_tlb_pgtable(struct mm_struct *mm, + unsigned long uaddr) +{ + unsigned long addr = uaddr >> 12 | ((unsigned long)ASID(mm) << 48); + + dsb(ishst); + asm("tlbi vae1is, %0" : : "r" (addr)); + dsb(ish); +} /* * On AArch64, the cache coherency is handled via the set_pte_at() function. */ From 135d31d1f9790bb02fc3ddd0249036a465f21255 Mon Sep 17 00:00:00 2001 From: Tommi Rantala Date: Mon, 2 Mar 2015 21:36:07 +0200 Subject: [PATCH 384/788] drm/radeon: fix DRM_IOCTL_RADEON_CS oops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit a28b2a47edcd0cb7c051b445f71a426000394606 upstream. Passing zeroed drm_radeon_cs struct to DRM_IOCTL_RADEON_CS produces the following oops. Fix by always calling INIT_LIST_HEAD() to avoid the crash in list_sort(). ---------------------------------- #include #include #include #include #include static const struct drm_radeon_cs cs; int main(int argc, char **argv) { return ioctl(open(argv[1], O_RDWR), DRM_IOCTL_RADEON_CS, &cs); } ---------------------------------- [ttrantal@test2 ~]$ ./main /dev/dri/card0 [ 46.904650] BUG: unable to handle kernel NULL pointer dereference at (null) [ 46.905022] IP: [] list_sort+0x42/0x240 [ 46.905022] PGD 68f29067 PUD 688b5067 PMD 0 [ 46.905022] Oops: 0002 [#1] SMP [ 46.905022] CPU: 0 PID: 2413 Comm: main Not tainted 4.0.0-rc1+ #58 [ 46.905022] Hardware name: Hewlett-Packard HP Compaq dc5750 Small Form Factor/0A64h, BIOS 786E3 v02.10 01/25/2007 [ 46.905022] task: ffff880058e2bcc0 ti: ffff880058e64000 task.ti: ffff880058e64000 [ 46.905022] RIP: 0010:[] [] list_sort+0x42/0x240 [ 46.905022] RSP: 0018:ffff880058e67998 EFLAGS: 00010246 [ 46.905022] RAX: 0000000000000000 RBX: 0000000000000000 RCX: 0000000000000000 [ 46.905022] RDX: ffffffff81644410 RSI: ffff880058e67b40 RDI: ffff880058e67a58 [ 46.905022] RBP: ffff880058e67a88 R08: 0000000000000000 R09: 0000000000000000 [ 46.905022] R10: ffff880058e2bcc0 R11: ffffffff828e6ca0 R12: ffffffff81644410 [ 46.905022] R13: ffff8800694b8018 R14: 0000000000000000 R15: ffff880058e679b0 [ 46.905022] FS: 00007fdc65a65700(0000) GS:ffff88006d600000(0000) knlGS:0000000000000000 [ 46.905022] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 46.905022] CR2: 0000000000000000 CR3: 0000000058dd9000 CR4: 00000000000006f0 [ 46.905022] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 46.905022] DR3: 0000000000000000 DR6: 00000000ffff4ff0 DR7: 0000000000000400 [ 46.905022] Stack: [ 46.905022] ffff880058e67b40 ffff880058e2bcc0 ffff880058e67a78 0000000000000000 [ 46.905022] 0000000000000000 0000000000000000 0000000000000000 0000000000000000 [ 46.905022] 0000000000000000 0000000000000000 0000000000000000 0000000000000000 [ 46.905022] Call Trace: [ 46.905022] [] radeon_cs_parser_fini+0x195/0x220 [ 46.905022] [] radeon_cs_ioctl+0xa9/0x960 [ 46.905022] [] drm_ioctl+0x19c/0x640 [ 46.905022] [] ? trace_hardirqs_on_caller+0xfd/0x1c0 [ 46.905022] [] ? trace_hardirqs_on+0xd/0x10 [ 46.905022] [] radeon_drm_ioctl+0x46/0x80 [ 46.905022] [] do_vfs_ioctl+0x318/0x570 [ 46.905022] [] ? selinux_file_ioctl+0x56/0x110 [ 46.905022] [] SyS_ioctl+0x81/0xa0 [ 46.905022] [] system_call_fastpath+0x12/0x17 [ 46.905022] Code: 48 89 b5 10 ff ff ff 0f 84 03 01 00 00 4c 8d bd 28 ff ff ff 31 c0 48 89 fb b9 15 00 00 00 49 89 d4 4c 89 ff f3 48 ab 48 8b 46 08 <48> c7 00 00 00 00 00 48 8b 0e 48 85 c9 0f 84 7d 00 00 00 c7 85 [ 46.905022] RIP [] list_sort+0x42/0x240 [ 46.905022] RSP [ 46.905022] CR2: 0000000000000000 [ 47.149253] ---[ end trace 09576b4e8b2c20b8 ]--- Reviewed-by: Christian König Signed-off-by: Tommi Rantala Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/radeon_cs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c index c830863bc98aa0..26e7d2837b4150 100644 --- a/drivers/gpu/drm/radeon/radeon_cs.c +++ b/drivers/gpu/drm/radeon/radeon_cs.c @@ -256,11 +256,13 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data) u32 ring = RADEON_CS_RING_GFX; s32 priority = 0; + INIT_LIST_HEAD(&p->validated); + if (!cs->num_chunks) { return 0; } + /* get chunks */ - INIT_LIST_HEAD(&p->validated); p->idx = 0; p->ib.sa_bo = NULL; p->const_ib.sa_bo = NULL; From be89f21f8ceef2afcc85e21518649f5dbcf9568a Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 2 Mar 2015 20:42:53 -0500 Subject: [PATCH 385/788] drm/radeon: do a posting read in evergreen_set_irq commit c320bb5f6dc0cb88a811cbaf839303e0a3916a92 upstream. To make sure the writes go through the pci bridge. bug: https://bugzilla.kernel.org/show_bug.cgi?id=90741 Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/evergreen.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 85995b4e333875..c674f63d7f14ea 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -4589,6 +4589,9 @@ int evergreen_irq_set(struct radeon_device *rdev) WREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, afmt5); WREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, afmt6); + /* posting read */ + RREG32(SRBM_STATUS); + return 0; } From bac9045923dbc5b60df81b6c2d8e3fc4daf70c3e Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 2 Mar 2015 20:36:26 -0500 Subject: [PATCH 386/788] drm/radeon: do a posting read in r100_set_irq commit f957063fee6392bb9365370db6db74dc0b2dce0a upstream. To make sure the writes go through the pci bridge. bug: https://bugzilla.kernel.org/show_bug.cgi?id=90741 Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/r100.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index 279801ca5110af..04f2514f756453 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -728,6 +728,10 @@ int r100_irq_set(struct radeon_device *rdev) tmp |= RADEON_FP2_DETECT_MASK; } WREG32(RADEON_GEN_INT_CNTL, tmp); + + /* read back to post the write */ + RREG32(RADEON_GEN_INT_CNTL); + return 0; } From 9a50805b75a288d22bf9830491c724909a700039 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 2 Mar 2015 20:41:31 -0500 Subject: [PATCH 387/788] drm/radeon: do a posting read in r600_set_irq commit 9d1393f23d5656cdd5f368efd60694d4aeed81d3 upstream. To make sure the writes go through the pci bridge. bug: https://bugzilla.kernel.org/show_bug.cgi?id=90741 Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/r600.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index ef5d6066fa5bbb..0e2cf2a3795b88 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -3783,6 +3783,9 @@ int r600_irq_set(struct radeon_device *rdev) WREG32(RV770_CG_THERMAL_INT, thermal_int); } + /* posting read */ + RREG32(R_000E50_SRBM_STATUS); + return 0; } From 0691e0b58b653027ce2580ef4c4f7a42929333dc Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 2 Mar 2015 20:45:24 -0500 Subject: [PATCH 388/788] drm/radeon: do a posting read in cik_set_irq commit cffefd9bb31cd35ab745d3b49005d10616d25bdc upstream. To make sure the writes go through the pci bridge. bug: https://bugzilla.kernel.org/show_bug.cgi?id=90741 Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/cik.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index de9a56205f0a1e..53b9ac3f81212e 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -7526,6 +7526,9 @@ int cik_irq_set(struct radeon_device *rdev) WREG32(DC_HPD5_INT_CONTROL, hpd5); WREG32(DC_HPD6_INT_CONTROL, hpd6); + /* posting read */ + RREG32(SRBM_STATUS); + return 0; } From 9a524a391964556eabe3efde6c5e4f9d6607a2e6 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 2 Mar 2015 20:43:53 -0500 Subject: [PATCH 389/788] drm/radeon: do a posting read in si_set_irq commit 0586915ec10d0ae60de5cd3381ad25a704760402 upstream. To make sure the writes go through the pci bridge. bug: https://bugzilla.kernel.org/show_bug.cgi?id=90741 Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/si.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c index 5d89b874a1a258..3045e28f4c8f24 100644 --- a/drivers/gpu/drm/radeon/si.c +++ b/drivers/gpu/drm/radeon/si.c @@ -6198,6 +6198,9 @@ int si_irq_set(struct radeon_device *rdev) WREG32(CG_THERMAL_INT, thermal_int); + /* posting read */ + RREG32(SRBM_STATUS); + return 0; } From 1f9e4e18abce492228554ee1957d5dc0fe483520 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 2 Mar 2015 20:39:56 -0500 Subject: [PATCH 390/788] drm/radeon: do a posting read in rs600_set_irq commit 54acf107e4e66d1f4a697e08a7f60dba9fcf07c3 upstream. To make sure the writes go through the pci bridge. bug: https://bugzilla.kernel.org/show_bug.cgi?id=90741 Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/rs600.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c index 74bce91aecc118..039660662ee81d 100644 --- a/drivers/gpu/drm/radeon/rs600.c +++ b/drivers/gpu/drm/radeon/rs600.c @@ -693,6 +693,10 @@ int rs600_irq_set(struct radeon_device *rdev) WREG32(R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL, hpd2); if (ASIC_IS_DCE2(rdev)) WREG32(R_007408_HDMI0_AUDIO_PACKET_CONTROL, hdmi0); + + /* posting read */ + RREG32(R_000040_GEN_INT_CNTL); + return 0; } From 7d590b172f784c93617bbd79bc1f98b2b074353a Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 3 Mar 2015 17:00:43 -0500 Subject: [PATCH 391/788] drm/radeon: fix interlaced modes on DCE8 commit 77ae5f4b48a0445426c9c1ef7c0f28b717e35d55 upstream. Need to double the viewport height. Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/atombios_crtc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index ed644a4f6f57c4..86807ee91bd13a 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -1405,6 +1405,9 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc, (x << 16) | y); viewport_w = crtc->mode.hdisplay; viewport_h = (crtc->mode.vdisplay + 1) & ~1; + if ((rdev->family >= CHIP_BONAIRE) && + (crtc->mode.flags & DRM_MODE_FLAG_INTERLACE)) + viewport_h *= 2; WREG32(EVERGREEN_VIEWPORT_SIZE + radeon_crtc->crtc_offset, (viewport_w << 16) | viewport_h); From 436b2edc6851c3e03a1886c35aea6664aab2c4ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Thu, 19 Feb 2015 09:40:28 +0100 Subject: [PATCH 392/788] drm/radeon: drop setting UPLL to sleep mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit a17d4996e051e78d164989b894608cf37cd5110b upstream. Just keep it working, seems to fix some PLL problems. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=73378 Signed-off-by: Christian König Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/si.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c index 3045e28f4c8f24..eed21db73e5941 100644 --- a/drivers/gpu/drm/radeon/si.c +++ b/drivers/gpu/drm/radeon/si.c @@ -7121,8 +7121,7 @@ int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_BYPASS_EN_MASK, ~UPLL_BYPASS_EN_MASK); if (!vclk || !dclk) { - /* keep the Bypass mode, put PLL to sleep */ - WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_SLEEP_MASK, ~UPLL_SLEEP_MASK); + /* keep the Bypass mode */ return 0; } @@ -7138,8 +7137,7 @@ int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) /* set VCO_MODE to 1 */ WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_VCO_MODE_MASK, ~UPLL_VCO_MODE_MASK); - /* toggle UPLL_SLEEP to 1 then back to 0 */ - WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_SLEEP_MASK, ~UPLL_SLEEP_MASK); + /* disable sleep mode */ WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_SLEEP_MASK); /* deassert UPLL_RESET */ From c7743e0e15d845e740b691df76e39dcb1bc7a1b3 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 3 Mar 2015 09:56:42 +0100 Subject: [PATCH 393/788] drm/radeon: fix wait to actually occur after the signaling callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit b6610101718d4ab90d793c482625e98eb1262cad upstream. A normal wait adds to the front of the tail. By doing something similar to fence_default_wait the fence code can run without racing. This is a complete fix for "panic on suspend from KDE with radeon", and a partial fix for "Radeon: System pauses on TAHITI". On tahiti si_irq_set needs to be fixed too, to completely flush the writes before radeon_fence_activity is called in radeon_fence_enable_signaling. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=90741 Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=90861 Signed-off-by: Maarten Lankhorst Reported-by: Jon Arne Jørgensen Reported-and-tested-by: Gustaw Smolarczyk Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/radeon_fence.c | 68 ++++++++++++++++++--------- 1 file changed, 45 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_fence.c b/drivers/gpu/drm/radeon/radeon_fence.c index d13d1b5a859f5b..df09ca7c488949 100644 --- a/drivers/gpu/drm/radeon/radeon_fence.c +++ b/drivers/gpu/drm/radeon/radeon_fence.c @@ -1030,37 +1030,59 @@ static inline bool radeon_test_signaled(struct radeon_fence *fence) return test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->base.flags); } +struct radeon_wait_cb { + struct fence_cb base; + struct task_struct *task; +}; + +static void +radeon_fence_wait_cb(struct fence *fence, struct fence_cb *cb) +{ + struct radeon_wait_cb *wait = + container_of(cb, struct radeon_wait_cb, base); + + wake_up_process(wait->task); +} + static signed long radeon_fence_default_wait(struct fence *f, bool intr, signed long t) { struct radeon_fence *fence = to_radeon_fence(f); struct radeon_device *rdev = fence->rdev; - bool signaled; + struct radeon_wait_cb cb; - fence_enable_sw_signaling(&fence->base); + cb.task = current; - /* - * This function has to return -EDEADLK, but cannot hold - * exclusive_lock during the wait because some callers - * may already hold it. This means checking needs_reset without - * lock, and not fiddling with any gpu internals. - * - * The callback installed with fence_enable_sw_signaling will - * run before our wait_event_*timeout call, so we will see - * both the signaled fence and the changes to needs_reset. - */ + if (fence_add_callback(f, &cb.base, radeon_fence_wait_cb)) + return t; + + while (t > 0) { + if (intr) + set_current_state(TASK_INTERRUPTIBLE); + else + set_current_state(TASK_UNINTERRUPTIBLE); + + /* + * radeon_test_signaled must be called after + * set_current_state to prevent a race with wake_up_process + */ + if (radeon_test_signaled(fence)) + break; + + if (rdev->needs_reset) { + t = -EDEADLK; + break; + } + + t = schedule_timeout(t); + + if (t > 0 && intr && signal_pending(current)) + t = -ERESTARTSYS; + } + + __set_current_state(TASK_RUNNING); + fence_remove_callback(f, &cb.base); - if (intr) - t = wait_event_interruptible_timeout(rdev->fence_queue, - ((signaled = radeon_test_signaled(fence)) || - rdev->needs_reset), t); - else - t = wait_event_timeout(rdev->fence_queue, - ((signaled = radeon_test_signaled(fence)) || - rdev->needs_reset), t); - - if (t > 0 && !signaled) - return -EDEADLK; return t; } From e411e5794a33d3b80f57b21c384e37ff06389ea2 Mon Sep 17 00:00:00 2001 From: Ben Goz Date: Sun, 8 Mar 2015 14:15:16 +0200 Subject: [PATCH 394/788] drm/radeon: Changing number of compute pipe lines commit e405ca3a1bf166f741506c07c2a277b5d48af8f7 upstream. The current CP firmware can handle Usermode Queues only on MEC1. To reflect this firmware change, this commit reduces number of compute pipelines to 4 - 1, from 8 - 1 (the first pipeline is allocated for kgd). Signed-off-by: Ben Goz Signed-off-by: Oded Gabbay Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/radeon_kfd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/radeon/radeon_kfd.c b/drivers/gpu/drm/radeon/radeon_kfd.c index bef9a09532844b..2151cec4e2c816 100644 --- a/drivers/gpu/drm/radeon/radeon_kfd.c +++ b/drivers/gpu/drm/radeon/radeon_kfd.c @@ -152,7 +152,7 @@ void radeon_kfd_device_init(struct radeon_device *rdev) .compute_vmid_bitmap = 0xFF00, .first_compute_pipe = 1, - .compute_pipe_count = 8 - 1, + .compute_pipe_count = 4 - 1, }; radeon_doorbell_get_kfd_info(rdev, From 0cd0c3867310fe333cd1d035693c273983cbe4ed Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 17 Mar 2015 11:53:33 -0400 Subject: [PATCH 395/788] drm/radeon: drop ttm two ended allocation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit a239118a24b3bf9089751068e431dfb63dc4168b upstream. radeon_bo_create() calls radeon_ttm_placement_from_domain() before ttm_bo_init() is called. radeon_ttm_placement_from_domain() uses the ttm bo size to determine when to select top down allocation but since the ttm bo is not initialized yet the check is always false. It only took effect when buffers were validated later. It also seemed to regress suspend and resume on some systems possibly due to it not taking effect in radeon_bo_create(). radeon_bo_create() and radeon_ttm_placement_from_domain() need to be reworked substantially for this to be optimally effective. Re-enable it at that point. Noticed-by: Oded Gabbay Reviewed-by: Christian König Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/radeon_object.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c index 040d2847f8e844..24f7d30b239d94 100644 --- a/drivers/gpu/drm/radeon/radeon_object.c +++ b/drivers/gpu/drm/radeon/radeon_object.c @@ -173,17 +173,6 @@ void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain) else rbo->placements[i].lpfn = 0; } - - /* - * Use two-ended allocation depending on the buffer size to - * improve fragmentation quality. - * 512kb was measured as the most optimal number. - */ - if (rbo->tbo.mem.size > 512 * 1024) { - for (i = 0; i < c; i++) { - rbo->placements[i].flags |= TTM_PL_FLAG_TOPDOWN; - } - } } int radeon_bo_create(struct radeon_device *rdev, From 5a2581b963c7a27e60b39edad05ffb125b51651a Mon Sep 17 00:00:00 2001 From: JeHyeon Yeon Date: Mon, 16 Mar 2015 01:03:19 +0000 Subject: [PATCH 396/788] LZ4 : fix the data abort issue commit d5e7cafd69da24e6d6cc988fab6ea313a2577efc upstream. If the part of the compression data are corrupted, or the compression data is totally fake, the memory access over the limit is possible. This is the log from my system usning lz4 decompression. [6502]data abort, halting [6503]r0 0x00000000 r1 0x00000000 r2 0xdcea0ffc r3 0xdcea0ffc [6509]r4 0xb9ab0bfd r5 0xdcea0ffc r6 0xdcea0ff8 r7 0xdce80000 [6515]r8 0x00000000 r9 0x00000000 r10 0x00000000 r11 0xb9a98000 [6522]r12 0xdcea1000 usp 0x00000000 ulr 0x00000000 pc 0x820149bc [6528]spsr 0x400001f3 and the memory addresses of some variables at the moment are ref:0xdcea0ffc, op:0xdcea0ffc, oend:0xdcea1000 As you can see, COPYLENGH is 8bytes, so @ref and @op can access the momory over @oend. Signed-off-by: JeHyeon Yeon Reviewed-by: David Sterba Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- lib/lz4/lz4_decompress.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/lz4/lz4_decompress.c b/lib/lz4/lz4_decompress.c index 7a85967060a518..f0f5c5c3de12e1 100644 --- a/lib/lz4/lz4_decompress.c +++ b/lib/lz4/lz4_decompress.c @@ -139,6 +139,9 @@ static int lz4_uncompress(const char *source, char *dest, int osize) /* Error: request to write beyond destination buffer */ if (cpy > oend) goto _output_error; + if ((ref + COPYLENGTH) > oend || + (op + COPYLENGTH) > oend) + goto _output_error; LZ4_SECURECOPY(ref, op, (oend - COPYLENGTH)); while (op < cpy) *op++ = *ref++; From ac29982167d67ca5853ebfa4cc9cd8e08b2382a1 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 26 Feb 2015 11:45:47 +0100 Subject: [PATCH 397/788] fuse: set stolen page uptodate commit aa991b3b267e24f578bac7b09cc57579b660304b upstream. Regular pipe buffers' ->steal method (generic_pipe_buf_steal()) doesn't set PG_uptodate. Don't warn on this condition, just set the uptodate flag. Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index ed19a7d622fa35..816086d5f116b5 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -890,8 +890,8 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep) newpage = buf->page; - if (WARN_ON(!PageUptodate(newpage))) - return -EIO; + if (!PageUptodate(newpage)) + SetPageUptodate(newpage); ClearPageMappedToDisk(newpage); From ec1b2f10f52ad6446247b1b24bb7d357a320a8e2 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 26 Feb 2015 11:45:47 +0100 Subject: [PATCH 398/788] fuse: notify: don't move pages commit 0d2783626a53d4c922f82d51fa675cb5d13f0d36 upstream. fuse_try_move_page() is not prepared for replacing pages that have already been read. Reported-by: Al Viro Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dev.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 816086d5f116b5..71c4619af33306 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1797,6 +1797,9 @@ static int fuse_notify_retrieve(struct fuse_conn *fc, unsigned int size, static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, unsigned int size, struct fuse_copy_state *cs) { + /* Don't try to move pages (yet) */ + cs->move_pages = 0; + switch (code) { case FUSE_NOTIFY_POLL: return fuse_notify_poll(fc, size, cs); From f582a6d95acfe93c7773b66f344aa898edf64fbd Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sun, 1 Mar 2015 10:18:16 -0500 Subject: [PATCH 399/788] serial: core: Fix iotype userspace breakage commit 2bb785169e9709d41220e5c18b0270883a82f85c upstream. commit 3ffb1a8193bea ("serial: core: Add big-endian iotype") re-numbered userspace-dependent values; ioctl(TIOCSSERIAL) can assign the port iotype (which is expected to match the selected i/o accessors), so iotype values must not be changed. Cc: Kevin Cernekee Signed-off-by: Peter Hurley Reviewed-by: Kevin Cernekee Signed-off-by: Greg Kroah-Hartman --- include/linux/serial_core.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 057038cf27880d..b405a62d1d4be1 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -146,9 +146,9 @@ struct uart_port { #define UPIO_HUB6 (1) /* Hub6 ISA card */ #define UPIO_MEM (2) /* 8b MMIO access */ #define UPIO_MEM32 (3) /* 32b little endian */ -#define UPIO_MEM32BE (4) /* 32b big endian */ -#define UPIO_AU (5) /* Au1x00 and RT288x type IO */ -#define UPIO_TSI (6) /* Tsi108/109 type IO */ +#define UPIO_AU (4) /* Au1x00 and RT288x type IO */ +#define UPIO_TSI (5) /* Tsi108/109 type IO */ +#define UPIO_MEM32BE (6) /* 32b big endian */ unsigned int read_status_mask; /* driver specific */ unsigned int ignore_status_mask; /* driver specific */ From 180c14a69855892bc5bb6be2deaa8a9f4147f85a Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 11 Mar 2015 09:19:16 -0400 Subject: [PATCH 400/788] serial: 8250_dw: Fix deadlock in LCR workaround commit 7fd6f640f2dd17dac6ddd6702c378cb0bb9cfa11 upstream. Trying to write console output from within the serial console driver while the port->lock is held causes recursive deadlock: CPU 0 spin_lock_irqsave(&port->lock) printk() console_unlock() call_console_drivers() serial8250_console_write() spin_lock_irqsave(&port->lock) ** DEADLOCK ** The 8250_dw i/o accessors try to write a console error message if the LCR workaround was unsuccessful. When the port->lock is already held (eg., when called from serial8250_set_termios()), this deadlocks. Make the error message a FIXME until a general solution is devised. Cc: Tim Kryger Reported-by: Zhang Zhen Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 555de07db593a1..7d89da349c3b47 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -111,7 +111,10 @@ static void dw8250_serial_out(struct uart_port *p, int offset, int value) dw8250_force_idle(p); writeb(value, p->membase + (UART_LCR << p->regshift)); } - dev_err(p->dev, "Couldn't set LCR to %d\n", value); + /* + * FIXME: this deadlocks if port->lock is already held + * dev_err(p->dev, "Couldn't set LCR to %d\n", value); + */ } } @@ -155,7 +158,10 @@ static void dw8250_serial_outq(struct uart_port *p, int offset, int value) __raw_writeq(value & 0xff, p->membase + (UART_LCR << p->regshift)); } - dev_err(p->dev, "Couldn't set LCR to %d\n", value); + /* + * FIXME: this deadlocks if port->lock is already held + * dev_err(p->dev, "Couldn't set LCR to %d\n", value); + */ } } #endif /* CONFIG_64BIT */ @@ -179,7 +185,10 @@ static void dw8250_serial_out32(struct uart_port *p, int offset, int value) dw8250_force_idle(p); writel(value, p->membase + (UART_LCR << p->regshift)); } - dev_err(p->dev, "Couldn't set LCR to %d\n", value); + /* + * FIXME: this deadlocks if port->lock is already held + * dev_err(p->dev, "Couldn't set LCR to %d\n", value); + */ } } From 2cf6258c282608633c9477e5c8e319e948565122 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sun, 1 Mar 2015 10:11:05 -0500 Subject: [PATCH 401/788] console: Fix console name size mismatch commit 30a22c215a0007603ffc08021f2e8b64018517dd upstream. commit 6ae9200f2cab7 ("enlarge console.name") increased the storage for the console name to 16 bytes, but not the corresponding struct console_cmdline::name storage. Console names longer than 8 bytes cause read beyond end-of-string and failure to match console; I'm not sure if there are other unexpected consequences. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- kernel/printk/console_cmdline.h | 2 +- kernel/printk/printk.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/printk/console_cmdline.h b/kernel/printk/console_cmdline.h index cbd69d84234117..2ca4a8b5fe5796 100644 --- a/kernel/printk/console_cmdline.h +++ b/kernel/printk/console_cmdline.h @@ -3,7 +3,7 @@ struct console_cmdline { - char name[8]; /* Name of the driver */ + char name[16]; /* Name of the driver */ int index; /* Minor dev. to use */ char *options; /* Options for the driver */ #ifdef CONFIG_A11Y_BRAILLE_CONSOLE diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index fae29e3ffbf027..2cdd35302af884 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -2464,6 +2464,7 @@ void register_console(struct console *newcon) for (i = 0, c = console_cmdline; i < MAX_CMDLINECONSOLES && c->name[0]; i++, c++) { + BUILD_BUG_ON(sizeof(c->name) != sizeof(newcon->name)); if (strcmp(c->name, newcon->name) != 0) continue; if (newcon->index >= 0 && From 3bb2dae3970b697ac296555cfe65512575bb245d Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 5 Mar 2015 10:45:30 +1030 Subject: [PATCH 402/788] virtio_console: init work unconditionally commit 4f6e24ed9de8634d6471ef86b382cba6d4e57ca8 upstream. when multiport is off, we don't initialize config work, but we then cancel uninitialized control_work on freeze. Signed-off-by: Michael S. Tsirkin Reviewed-by: Amit Shah Signed-off-by: Rusty Russell Signed-off-by: Greg Kroah-Hartman --- drivers/char/virtio_console.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index de03df9dd7c961..b9eae4b6236ba4 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -2031,12 +2031,13 @@ static int virtcons_probe(struct virtio_device *vdev) virtio_device_ready(portdev->vdev); + INIT_WORK(&portdev->control_work, &control_work_handler); + if (multiport) { unsigned int nr_added_bufs; spin_lock_init(&portdev->c_ivq_lock); spin_lock_init(&portdev->c_ovq_lock); - INIT_WORK(&portdev->control_work, &control_work_handler); nr_added_bufs = fill_queue(portdev->c_ivq, &portdev->c_ivq_lock); From 849de63a34f552fea2258a1e272e0ede4b8b30f6 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 5 Mar 2015 10:45:49 +1030 Subject: [PATCH 403/788] virtio_console: avoid config access from irq commit eeb8a7e8bb123e84daeef84f5a2eab99ad2839a2 upstream. when multiport is off, virtio console invokes config access from irq context, config access is blocking on s390. Fix this up by scheduling work from config irq - similar to what we do for multiport configs. Signed-off-by: Michael S. Tsirkin Reviewed-by: Amit Shah Signed-off-by: Rusty Russell Signed-off-by: Greg Kroah-Hartman --- drivers/char/virtio_console.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index b9eae4b6236ba4..c3aac4c1a5549b 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -142,6 +142,7 @@ struct ports_device { * notification */ struct work_struct control_work; + struct work_struct config_work; struct list_head ports; @@ -1837,10 +1838,21 @@ static void config_intr(struct virtio_device *vdev) portdev = vdev->priv; + if (!use_multiport(portdev)) + schedule_work(&portdev->config_work); +} + +static void config_work_handler(struct work_struct *work) +{ + struct ports_device *portdev; + + portdev = container_of(work, struct ports_device, control_work); if (!use_multiport(portdev)) { + struct virtio_device *vdev; struct port *port; u16 rows, cols; + vdev = portdev->vdev; virtio_cread(vdev, struct virtio_console_config, cols, &cols); virtio_cread(vdev, struct virtio_console_config, rows, &rows); @@ -2031,6 +2043,7 @@ static int virtcons_probe(struct virtio_device *vdev) virtio_device_ready(portdev->vdev); + INIT_WORK(&portdev->config_work, &config_work_handler); INIT_WORK(&portdev->control_work, &control_work_handler); if (multiport) { @@ -2105,6 +2118,8 @@ static void virtcons_remove(struct virtio_device *vdev) /* Finish up work that's lined up */ if (use_multiport(portdev)) cancel_work_sync(&portdev->control_work); + else + cancel_work_sync(&portdev->config_work); list_for_each_entry_safe(port, port2, &portdev->ports, list) unplug_port(port); @@ -2156,6 +2171,7 @@ static int virtcons_freeze(struct virtio_device *vdev) virtqueue_disable_cb(portdev->c_ivq); cancel_work_sync(&portdev->control_work); + cancel_work_sync(&portdev->config_work); /* * Once more: if control_work_handler() was running, it would * enable the cb as the last step. From 34deb2f30414b412271e08f63d045aa2b49be63e Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 6 Mar 2015 10:49:21 +0000 Subject: [PATCH 404/788] Change email address for 8250_pci commit f2e0ea861117bda073d1d7ffbd3120c07c0d5d34 upstream. I'm still receiving reports to my email address, so let's point this at the linux-serial mailing list instead. Signed-off-by: Russell King Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index d1f8dc6aabcbe5..1442956d5e9c2f 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -69,7 +69,7 @@ static void moan_device(const char *str, struct pci_dev *dev) "Please send the output of lspci -vv, this\n" "message (0x%04x,0x%04x,0x%04x,0x%04x), the\n" "manufacturer and name of serial board or\n" - "modem board to rmk+serial@arm.linux.org.uk.\n", + "modem board to .\n", pci_name(dev), str, dev->vendor, dev->device, dev->subsystem_vendor, dev->subsystem_device); } From 63056f67cb5389cb81225848770544dd88c437e6 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Wed, 4 Mar 2015 23:10:28 -0500 Subject: [PATCH 405/788] ftrace: Clear REGS_EN and TRAMP_EN flags on disabling record via sysctl commit b24d443b8f17d9776f5fc1f6c780a0a21eb02913 upstream. When /proc/sys/kernel/ftrace_enabled is set to zero, all function tracing is disabled. But the records that represent the functions still hold information about the ftrace_ops that are hooked to them. ftrace_ops may request "REGS" (have a full set of pt_regs passed to the callback), or "TRAMP" (the ops has its own trampoline to use). When the record is updated to represent the state of the ops hooked to it, it sets "REGS_EN" and/or "TRAMP_EN" to state that the callback points to the correct trampoline (REGS has its own trampoline). When ftrace_enabled is set to zero, all ftrace locations are a nop, so they do not point to any trampoline. But the _EN flags are still set. This can cause the accounting to go wrong when ftrace_enabled is cleared and an ops that has a trampoline is registered or unregistered. For example, the following will cause ftrace to crash: # echo function_graph > /sys/kernel/debug/tracing/current_tracer # echo 0 > /proc/sys/kernel/ftrace_enabled # echo nop > /sys/kernel/debug/tracing/current_tracer # echo 1 > /proc/sys/kernel/ftrace_enabled # echo function_graph > /sys/kernel/debug/tracing/current_tracer As function_graph uses a trampoline, when ftrace_enabled is set to zero the updates to the record are not done. When enabling function_graph again, the record will still have the TRAMP_EN flag set, and it will look for an op that has a trampoline other than the function_graph ops, and fail to find one. Reported-by: Pratyush Anand Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- kernel/trace/ftrace.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 224e768bdc738d..010ab93660eca2 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -2041,8 +2041,12 @@ static int ftrace_check_record(struct dyn_ftrace *rec, int enable, int update) if (!ftrace_rec_count(rec)) rec->flags = 0; else - /* Just disable the record (keep REGS state) */ - rec->flags &= ~FTRACE_FL_ENABLED; + /* + * Just disable the record, but keep the ops TRAMP + * and REGS states. The _EN flags must be disabled though. + */ + rec->flags &= ~(FTRACE_FL_ENABLED | FTRACE_FL_TRAMP_EN | + FTRACE_FL_REGS_EN); } return FTRACE_UPDATE_MAKE_NOP; From 0482042fbf909e92a24676ffe781d7f38fa5b134 Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Fri, 6 Mar 2015 23:58:06 +0530 Subject: [PATCH 406/788] ftrace: Fix en(dis)able graph caller when en(dis)abling record via sysctl commit 1619dc3f8f555ee1cdd3c75db3885d5715442b12 upstream. When ftrace is enabled globally through the proc interface, we must check if ftrace_graph_active is set. If it is set, then we should also pass the FTRACE_START_FUNC_RET command to ftrace_run_update_code(). Similarly, when ftrace is disabled globally through the proc interface, we must check if ftrace_graph_active is set. If it is set, then we should also pass the FTRACE_STOP_FUNC_RET command to ftrace_run_update_code(). Consider the following situation. # echo 0 > /proc/sys/kernel/ftrace_enabled After this ftrace_enabled = 0. # echo function_graph > /sys/kernel/debug/tracing/current_tracer Since ftrace_enabled = 0, ftrace_enable_ftrace_graph_caller() is never called. # echo 1 > /proc/sys/kernel/ftrace_enabled Now ftrace_enabled will be set to true, but still ftrace_enable_ftrace_graph_caller() will not be called, which is not desired. Further if we execute the following after this: # echo nop > /sys/kernel/debug/tracing/current_tracer Now since ftrace_enabled is set it will call ftrace_disable_ftrace_graph_caller(), which causes a kernel warning on the ARM platform. On the ARM platform, when ftrace_enable_ftrace_graph_caller() is called, it checks whether the old instruction is a nop or not. If it's not a nop, then it returns an error. If it is a nop then it replaces instruction at that address with a branch to ftrace_graph_caller. ftrace_disable_ftrace_graph_caller() behaves just the opposite. Therefore, if generic ftrace code ever calls either ftrace_enable_ftrace_graph_caller() or ftrace_disable_ftrace_graph_caller() consecutively two times in a row, then it will return an error, which will cause the generic ftrace code to raise a warning. Note, x86 does not have an issue with this because the architecture specific code for ftrace_enable_ftrace_graph_caller() and ftrace_disable_ftrace_graph_caller() does not check the previous state, and calling either of these functions twice in a row has no ill effect. Link: http://lkml.kernel.org/r/e4fbe64cdac0dd0e86a3bf914b0f83c0b419f146.1425666454.git.panand@redhat.com Signed-off-by: Pratyush Anand [ removed extra if (ftrace_start_up) and defined ftrace_graph_active as 0 if CONFIG_FUNCTION_GRAPH_TRACER is not set. ] Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- kernel/trace/ftrace.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 010ab93660eca2..54e9cf8c34a08a 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -1059,6 +1059,12 @@ static __init void ftrace_profile_debugfs(struct dentry *d_tracer) static struct pid * const ftrace_swapper_pid = &init_struct_pid; +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +static int ftrace_graph_active; +#else +# define ftrace_graph_active 0 +#endif + #ifdef CONFIG_DYNAMIC_FTRACE static struct ftrace_ops *removed_ops; @@ -2692,24 +2698,36 @@ static int ftrace_shutdown(struct ftrace_ops *ops, int command) static void ftrace_startup_sysctl(void) { + int command; + if (unlikely(ftrace_disabled)) return; /* Force update next time */ saved_ftrace_func = NULL; /* ftrace_start_up is true if we want ftrace running */ - if (ftrace_start_up) - ftrace_run_update_code(FTRACE_UPDATE_CALLS); + if (ftrace_start_up) { + command = FTRACE_UPDATE_CALLS; + if (ftrace_graph_active) + command |= FTRACE_START_FUNC_RET; + ftrace_run_update_code(command); + } } static void ftrace_shutdown_sysctl(void) { + int command; + if (unlikely(ftrace_disabled)) return; /* ftrace_start_up is true if ftrace is running */ - if (ftrace_start_up) - ftrace_run_update_code(FTRACE_DISABLE_CALLS); + if (ftrace_start_up) { + command = FTRACE_DISABLE_CALLS; + if (ftrace_graph_active) + command |= FTRACE_STOP_FUNC_RET; + ftrace_run_update_code(command); + } } static cycle_t ftrace_update_time; @@ -5594,8 +5612,6 @@ static struct ftrace_ops graph_ops = { ASSIGN_OPS_HASH(graph_ops, &global_ops.local_hash) }; -static int ftrace_graph_active; - int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace) { return 0; From 7ae4221acf2233d4209209854bae04d7f6e3592d Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Fri, 6 Mar 2015 19:55:13 -0500 Subject: [PATCH 407/788] ftrace: Fix ftrace enable ordering of sysctl ftrace_enabled commit 524a38682573b2e15ab6317ccfe50280441514be upstream. Some archs (specifically PowerPC), are sensitive with the ordering of the enabling of the calls to function tracing and setting of the function to use to be traced. That is, update_ftrace_function() sets what function the ftrace_caller trampoline should call. Some archs require this to be set before calling ftrace_run_update_code(). Another bug was discovered, that ftrace_startup_sysctl() called ftrace_run_update_code() directly. If the function the ftrace_caller trampoline changes, then it will not be updated. Instead a call to ftrace_startup_enable() should be called because it tests to see if the callback changed since the code was disabled, and will tell the arch to update appropriately. Most archs do not need this notification, but PowerPC does. The problem could be seen by the following commands: # echo 0 > /proc/sys/kernel/ftrace_enabled # echo function > /sys/kernel/debug/tracing/current_tracer # echo 1 > /proc/sys/kernel/ftrace_enabled # cat /sys/kernel/debug/tracing/trace The trace will show that function tracing was not active. Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- kernel/trace/ftrace.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 54e9cf8c34a08a..af5bffd1053c3b 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -2710,7 +2710,7 @@ static void ftrace_startup_sysctl(void) command = FTRACE_UPDATE_CALLS; if (ftrace_graph_active) command |= FTRACE_START_FUNC_RET; - ftrace_run_update_code(command); + ftrace_startup_enable(command); } } @@ -5580,12 +5580,12 @@ ftrace_enable_sysctl(struct ctl_table *table, int write, if (ftrace_enabled) { - ftrace_startup_sysctl(); - /* we are starting ftrace again */ if (ftrace_ops_list != &ftrace_list_end) update_ftrace_function(); + ftrace_startup_sysctl(); + } else { /* stopping ftrace calls (just send to ftrace_stub) */ ftrace_trace_function = ftrace_stub; From 1db8d02526e3e224c7dc4df2b2cda7cf37a9ae80 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Mon, 23 Feb 2015 20:37:54 +0100 Subject: [PATCH 408/788] can: add missing initialisations in CAN related skbuffs commit 969439016d2cf61fef53a973d7e6d2061c3793b1 upstream. When accessing CAN network interfaces with AF_PACKET sockets e.g. by dhclient this can lead to a skb_under_panic due to missing skb initialisations. Add the missing initialisations at the CAN skbuff creation times on driver level (rx path) and in the network layer (tx path). Reported-by: Austin Schuh Reported-by: Daniel Steer Signed-off-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/dev.c | 8 ++++++++ net/can/af_can.c | 3 +++ 2 files changed, 11 insertions(+) diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c index 847c1f813261d9..62ca0e8e1fbcf4 100644 --- a/drivers/net/can/dev.c +++ b/drivers/net/can/dev.c @@ -578,6 +578,10 @@ struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf) skb->pkt_type = PACKET_BROADCAST; skb->ip_summed = CHECKSUM_UNNECESSARY; + skb_reset_mac_header(skb); + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + can_skb_reserve(skb); can_skb_prv(skb)->ifindex = dev->ifindex; @@ -602,6 +606,10 @@ struct sk_buff *alloc_canfd_skb(struct net_device *dev, skb->pkt_type = PACKET_BROADCAST; skb->ip_summed = CHECKSUM_UNNECESSARY; + skb_reset_mac_header(skb); + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + can_skb_reserve(skb); can_skb_prv(skb)->ifindex = dev->ifindex; diff --git a/net/can/af_can.c b/net/can/af_can.c index 66e08040ced755..32d710eaf1fc99 100644 --- a/net/can/af_can.c +++ b/net/can/af_can.c @@ -259,6 +259,9 @@ int can_send(struct sk_buff *skb, int loop) goto inval_skb; } + skb->ip_summed = CHECKSUM_UNNECESSARY; + + skb_reset_mac_header(skb); skb_reset_network_header(skb); skb_reset_transport_header(skb); From add9df23fcbaff2c77cedb0056ef08159420a1a8 Mon Sep 17 00:00:00 2001 From: "Ahmed S. Darwish" Date: Thu, 26 Feb 2015 10:22:02 -0500 Subject: [PATCH 409/788] can: kvaser_usb: Read all messages in a bulk-in URB buffer commit 2fec5104f9c61de4cf2205aa355101e19a81f490 upstream. The Kvaser firmware can only read and write messages that are not crossing the USB endpoint's wMaxPacketSize boundary. While receiving commands from the CAN device, if the next command in the same URB buffer crossed that max packet size boundary, the firmware puts a zero-length placeholder command in its place then moves the real command to the next boundary mark. The driver did not recognize such behavior, leading to missing a good number of rx events during a heavy rx load session. Moreover, a tx URB context only gets freed upon receiving its respective tx ACK event. Over time, the free tx URB contexts pool gets depleted due to the missing ACK events. Consequently, the netif transmission queue gets __permanently__ stopped; no frames could be sent again except after restarting the CAN newtwork interface. Signed-off-by: Ahmed S. Darwish Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/usb/kvaser_usb.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c index 7af379ca861b11..913a2bc8598dae 100644 --- a/drivers/net/can/usb/kvaser_usb.c +++ b/drivers/net/can/usb/kvaser_usb.c @@ -12,6 +12,7 @@ * Copyright (C) 2012 Olivier Sobrie */ +#include #include #include #include @@ -403,8 +404,15 @@ static int kvaser_usb_wait_msg(const struct kvaser_usb *dev, u8 id, while (pos <= actual_len - MSG_HEADER_LEN) { tmp = buf + pos; - if (!tmp->len) - break; + /* Handle messages crossing the USB endpoint max packet + * size boundary. Check kvaser_usb_read_bulk_callback() + * for further details. + */ + if (tmp->len == 0) { + pos = round_up(pos, + dev->bulk_in->wMaxPacketSize); + continue; + } if (pos + tmp->len > actual_len) { dev_err(dev->udev->dev.parent, @@ -980,8 +988,19 @@ static void kvaser_usb_read_bulk_callback(struct urb *urb) while (pos <= urb->actual_length - MSG_HEADER_LEN) { msg = urb->transfer_buffer + pos; - if (!msg->len) - break; + /* The Kvaser firmware can only read and write messages that + * does not cross the USB's endpoint wMaxPacketSize boundary. + * If a follow-up command crosses such boundary, firmware puts + * a placeholder zero-length command in its place then aligns + * the real command to the next max packet size. + * + * Handle such cases or we're going to miss a significant + * number of events in case of a heavy rx load on the bus. + */ + if (msg->len == 0) { + pos = round_up(pos, dev->bulk_in->wMaxPacketSize); + continue; + } if (pos + msg->len > urb->actual_length) { dev_err(dev->udev->dev.parent, "Format error\n"); @@ -989,7 +1008,6 @@ static void kvaser_usb_read_bulk_callback(struct urb *urb) } kvaser_usb_handle_message(dev, msg); - pos += msg->len; } From 23bf13229536c8f7f5f4b0646a90a4cd11c97c01 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 5 Mar 2015 08:04:13 -0500 Subject: [PATCH 410/788] workqueue: fix hang involving racing cancel[_delayed]_work_sync()'s for PREEMPT_NONE commit 8603e1b30027f943cc9c1eef2b291d42c3347af1 upstream. cancel[_delayed]_work_sync() are implemented using __cancel_work_timer() which grabs the PENDING bit using try_to_grab_pending() and then flushes the work item with PENDING set to prevent the on-going execution of the work item from requeueing itself. try_to_grab_pending() can always grab PENDING bit without blocking except when someone else is doing the above flushing during cancelation. In that case, try_to_grab_pending() returns -ENOENT. In this case, __cancel_work_timer() currently invokes flush_work(). The assumption is that the completion of the work item is what the other canceling task would be waiting for too and thus waiting for the same condition and retrying should allow forward progress without excessive busy looping Unfortunately, this doesn't work if preemption is disabled or the latter task has real time priority. Let's say task A just got woken up from flush_work() by the completion of the target work item. If, before task A starts executing, task B gets scheduled and invokes __cancel_work_timer() on the same work item, its try_to_grab_pending() will return -ENOENT as the work item is still being canceled by task A and flush_work() will also immediately return false as the work item is no longer executing. This puts task B in a busy loop possibly preventing task A from executing and clearing the canceling state on the work item leading to a hang. task A task B worker executing work __cancel_work_timer() try_to_grab_pending() set work CANCELING flush_work() block for work completion completion, wakes up A __cancel_work_timer() while (forever) { try_to_grab_pending() -ENOENT as work is being canceled flush_work() false as work is no longer executing } This patch removes the possible hang by updating __cancel_work_timer() to explicitly wait for clearing of CANCELING rather than invoking flush_work() after try_to_grab_pending() fails with -ENOENT. Link: http://lkml.kernel.org/g/20150206171156.GA8942@axis.com v3: bit_waitqueue() can't be used for work items defined in vmalloc area. Switched to custom wake function which matches the target work item and exclusive wait and wakeup. v2: v1 used wake_up() on bit_waitqueue() which leads to NULL deref if the target bit waitqueue has wait_bit_queue's on it. Use DEFINE_WAIT_BIT() and __wake_up_bit() instead. Reported by Tomeu Vizoso. Signed-off-by: Tejun Heo Reported-by: Rabin Vincent Cc: Tomeu Vizoso Tested-by: Jesper Nilsson Tested-by: Rabin Vincent Signed-off-by: Greg Kroah-Hartman --- include/linux/workqueue.h | 3 ++- kernel/workqueue.c | 56 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index b996e6cde6bb65..9eb54f41623e3a 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -70,7 +70,8 @@ enum { /* data contains off-queue information when !WORK_STRUCT_PWQ */ WORK_OFFQ_FLAG_BASE = WORK_STRUCT_COLOR_SHIFT, - WORK_OFFQ_CANCELING = (1 << WORK_OFFQ_FLAG_BASE), + __WORK_OFFQ_CANCELING = WORK_OFFQ_FLAG_BASE, + WORK_OFFQ_CANCELING = (1 << __WORK_OFFQ_CANCELING), /* * When a work item is off queue, its high bits point to the last diff --git a/kernel/workqueue.c b/kernel/workqueue.c index beeeac9e0e3e68..82d0c8d4fe484a 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -2728,19 +2728,57 @@ bool flush_work(struct work_struct *work) } EXPORT_SYMBOL_GPL(flush_work); +struct cwt_wait { + wait_queue_t wait; + struct work_struct *work; +}; + +static int cwt_wakefn(wait_queue_t *wait, unsigned mode, int sync, void *key) +{ + struct cwt_wait *cwait = container_of(wait, struct cwt_wait, wait); + + if (cwait->work != key) + return 0; + return autoremove_wake_function(wait, mode, sync, key); +} + static bool __cancel_work_timer(struct work_struct *work, bool is_dwork) { + static DECLARE_WAIT_QUEUE_HEAD(cancel_waitq); unsigned long flags; int ret; do { ret = try_to_grab_pending(work, is_dwork, &flags); /* - * If someone else is canceling, wait for the same event it - * would be waiting for before retrying. + * If someone else is already canceling, wait for it to + * finish. flush_work() doesn't work for PREEMPT_NONE + * because we may get scheduled between @work's completion + * and the other canceling task resuming and clearing + * CANCELING - flush_work() will return false immediately + * as @work is no longer busy, try_to_grab_pending() will + * return -ENOENT as @work is still being canceled and the + * other canceling task won't be able to clear CANCELING as + * we're hogging the CPU. + * + * Let's wait for completion using a waitqueue. As this + * may lead to the thundering herd problem, use a custom + * wake function which matches @work along with exclusive + * wait and wakeup. */ - if (unlikely(ret == -ENOENT)) - flush_work(work); + if (unlikely(ret == -ENOENT)) { + struct cwt_wait cwait; + + init_wait(&cwait.wait); + cwait.wait.func = cwt_wakefn; + cwait.work = work; + + prepare_to_wait_exclusive(&cancel_waitq, &cwait.wait, + TASK_UNINTERRUPTIBLE); + if (work_is_canceling(work)) + schedule(); + finish_wait(&cancel_waitq, &cwait.wait); + } } while (unlikely(ret < 0)); /* tell other tasks trying to grab @work to back off */ @@ -2749,6 +2787,16 @@ static bool __cancel_work_timer(struct work_struct *work, bool is_dwork) flush_work(work); clear_work_data(work); + + /* + * Paired with prepare_to_wait() above so that either + * waitqueue_active() is visible here or !work_is_canceling() is + * visible there. + */ + smp_mb(); + if (waitqueue_active(&cancel_waitq)) + __wake_up(&cancel_waitq, TASK_NORMAL, 1, work); + return ret; } From c50360e4aad98374e89cb1147a76fe0b959c612c Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Wed, 4 Mar 2015 09:56:02 -0500 Subject: [PATCH 411/788] seq_buf: Fix seq_buf_vprintf() truncation commit 4a8fe4e1811c96ad0ad9f4083f2fe4fb43b2988d upstream. In seq_buf_vprintf(), vsnprintf() is used to copy the format into the buffer remaining in the seq_buf structure. The return of vsnprintf() is the amount of characters written to the buffer excluding the '\0', unless the line was truncated! If the line copied does not fit, it is truncated, and a '\0' is added to the end of the buffer. But in this case, '\0' is included in the length of the line written. To know if the buffer had overflowed, the return length will be the same as the length of the buffer passed in. The check in seq_buf_vprintf() only checked if the length returned from vsnprintf() would fit in the buffer, as the seq_buf_vprintf() is only to be an all or nothing command. It either writes all the string into the seq_buf, or none of it. If the string is truncated, the pointers inside the seq_buf must be reset to what they were when the function was called. This is not the case. On overflow, it copies only part of the string. The fix is to change the overflow check to see if the length returned from vsnprintf() is less than the length remaining in the seq_buf buffer, and not if it is less than or equal to as it currently does. Then seq_buf_vprintf() will know if the write from vsnpritnf() was truncated or not. Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- lib/seq_buf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/seq_buf.c b/lib/seq_buf.c index 4eedfedb9e316e..795dd94c7fd7ba 100644 --- a/lib/seq_buf.c +++ b/lib/seq_buf.c @@ -61,7 +61,7 @@ int seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args) if (s->len < s->size) { len = vsnprintf(s->buffer + s->len, s->size - s->len, fmt, args); - if (seq_buf_can_fit(s, len)) { + if (s->len + len < s->size) { s->len += len; return 0; } From 0dab26655a339605c62387405d83ad02a4f25a1f Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Wed, 4 Mar 2015 23:30:45 -0500 Subject: [PATCH 412/788] seq_buf: Fix seq_buf_bprintf() truncation commit 4d4eb4d4fbd9403682e2b75117b6b895531d8e01 upstream. In seq_buf_bprintf(), bstr_printf() is used to copy the format into the buffer remaining in the seq_buf structure. The return of bstr_printf() is the amount of characters written to the buffer excluding the '\0', unless the line was truncated! If the line copied does not fit, it is truncated, and a '\0' is added to the end of the buffer. But in this case, '\0' is included in the length of the line written. To know if the buffer had overflowed, the return length will be the same or greater than the length of the buffer passed in. The check in seq_buf_bprintf() only checked if the length returned from bstr_printf() would fit in the buffer, as the seq_buf_bprintf() is only to be an all or nothing command. It either writes all the string into the seq_buf, or none of it. If the string is truncated, the pointers inside the seq_buf must be reset to what they were when the function was called. This is not the case. On overflow, it copies only part of the string. The fix is to change the overflow check to see if the length returned from bstr_printf() is less than the length remaining in the seq_buf buffer, and not if it is less than or equal to as it currently does. Then seq_buf_bprintf() will know if the write from bstr_printf() was truncated or not. Link: http://lkml.kernel.org/r/1425500481.2712.27.camel@perches.com Reported-by: Joe Perches Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- lib/seq_buf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/seq_buf.c b/lib/seq_buf.c index 795dd94c7fd7ba..f25c33b279f26c 100644 --- a/lib/seq_buf.c +++ b/lib/seq_buf.c @@ -154,7 +154,7 @@ int seq_buf_bprintf(struct seq_buf *s, const char *fmt, const u32 *binary) if (s->len < s->size) { ret = bstr_printf(s->buffer + s->len, len, fmt, binary); - if (seq_buf_can_fit(s, ret)) { + if (s->len + ret < s->size) { s->len += ret; return 0; } From fbbca5e8cc61da1665212b97745b9898eeab8045 Mon Sep 17 00:00:00 2001 From: Zefan Li Date: Fri, 13 Feb 2015 11:19:49 +0800 Subject: [PATCH 413/788] cpuset: initialize effective masks when clone_children is enabled commit 790317e1b266c776765a4bdcedefea706ff0fada upstream. If clone_children is enabled, effective masks won't be initialized due to the bug: # mount -t cgroup -o cpuset xxx /mnt # echo 1 > cgroup.clone_children # mkdir /mnt/tmp # cat /mnt/tmp/ # cat cpuset.effective_cpus # cat cpuset.cpus 0-15 And then this cpuset won't constrain the tasks in it. Either the bug or the fix has no effect on unified hierarchy, as there's no clone_chidren flag there any more. Reported-by: Christian Brauner Reported-by: Serge Hallyn Signed-off-by: Zefan Li Signed-off-by: Tejun Heo Tested-by: Serge Hallyn Signed-off-by: Greg Kroah-Hartman --- kernel/cpuset.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/cpuset.c b/kernel/cpuset.c index 64b257f6bca2e2..7e9d71130015d5 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -1992,7 +1992,9 @@ static int cpuset_css_online(struct cgroup_subsys_state *css) spin_lock_irq(&callback_lock); cs->mems_allowed = parent->mems_allowed; + cs->effective_mems = parent->mems_allowed; cpumask_copy(cs->cpus_allowed, parent->cpus_allowed); + cpumask_copy(cs->effective_cpus, parent->cpus_allowed); spin_unlock_irq(&callback_lock); out_unlock: mutex_unlock(&cpuset_mutex); From 9b1c6e3aac08700162cd8d0588b73e8a278b6e2f Mon Sep 17 00:00:00 2001 From: Zefan Li Date: Fri, 13 Feb 2015 11:20:30 +0800 Subject: [PATCH 414/788] cpuset: fix a warning when clearing configured masks in old hierarchy commit 79063bffc81f82689bd90e16da1b49408f3bf095 upstream. When we clear cpuset.cpus, cpuset.effective_cpus won't be cleared: # mount -t cgroup -o cpuset xxx /mnt # mkdir /mnt/tmp # echo 0 > /mnt/tmp/cpuset.cpus # echo > /mnt/tmp/cpuset.cpus # cat cpuset.cpus # cat cpuset.effective_cpus 0-15 And a kernel warning in update_cpumasks_hier() is triggered: ------------[ cut here ]------------ WARNING: CPU: 0 PID: 4028 at kernel/cpuset.c:894 update_cpumasks_hier+0x471/0x650() Signed-off-by: Zefan Li Signed-off-by: Tejun Heo Tested-by: Serge Hallyn Signed-off-by: Greg Kroah-Hartman --- kernel/cpuset.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/cpuset.c b/kernel/cpuset.c index 7e9d71130015d5..29463c26c83f96 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -873,7 +873,7 @@ static void update_cpumasks_hier(struct cpuset *cs, struct cpumask *new_cpus) * If it becomes empty, inherit the effective mask of the * parent, which is guaranteed to have some CPUs. */ - if (cpumask_empty(new_cpus)) + if (cgroup_on_dfl(cp->css.cgroup) && cpumask_empty(new_cpus)) cpumask_copy(new_cpus, parent->effective_cpus); /* Skip the whole subtree if the cpumask remains the same. */ @@ -1129,7 +1129,7 @@ static void update_nodemasks_hier(struct cpuset *cs, nodemask_t *new_mems) * If it becomes empty, inherit the effective mask of the * parent, which is guaranteed to have some MEMs. */ - if (nodes_empty(*new_mems)) + if (cgroup_on_dfl(cp->css.cgroup) && nodes_empty(*new_mems)) *new_mems = parent->effective_mems; /* Skip the whole subtree if the nodemask remains the same. */ From f8f48d976dd1d8b828aa48a08599da536fd9dfe5 Mon Sep 17 00:00:00 2001 From: Jason Low Date: Fri, 13 Feb 2015 11:58:07 +0800 Subject: [PATCH 415/788] cpuset: Fix cpuset sched_relax_domain_level commit 283cb41f426b723a0255702b761b0fc5d1b53a81 upstream. The cpuset.sched_relax_domain_level can control how far we do immediate load balancing on a system. However, it was found on recent kernels that echo'ing a value into cpuset.sched_relax_domain_level did not reduce any immediate load balancing. The reason this occurred was because the update_domain_attr_tree() traversal did not update for the "top_cpuset". This resulted in nothing being changed when modifying the sched_relax_domain_level parameter. This patch is able to address that problem by having update_domain_attr_tree() allow updates for the root in the cpuset traversal. Fixes: fc560a26acce ("cpuset: replace cpuset->stack_list with cpuset_for_each_descendant_pre()") Signed-off-by: Jason Low Signed-off-by: Zefan Li Signed-off-by: Tejun Heo Tested-by: Serge Hallyn Signed-off-by: Greg Kroah-Hartman --- kernel/cpuset.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/kernel/cpuset.c b/kernel/cpuset.c index 29463c26c83f96..9e255991410c14 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -548,9 +548,6 @@ static void update_domain_attr_tree(struct sched_domain_attr *dattr, rcu_read_lock(); cpuset_for_each_descendant_pre(cp, pos_css, root_cs) { - if (cp == root_cs) - continue; - /* skip the whole subtree if @cp doesn't have any CPU */ if (cpumask_empty(cp->cpus_allowed)) { pos_css = css_rightmost_descendant(pos_css); From 974fb1864df486455223d25bb3ec51af79ff4d6b Mon Sep 17 00:00:00 2001 From: "jmlatten@linux.vnet.ibm.com" Date: Fri, 20 Feb 2015 18:11:24 -0600 Subject: [PATCH 416/788] tpm/ibmvtpm: Additional LE support for tpm_ibmvtpm_send commit 62dfd912ab3b5405b6fe72d0135c37e9648071f1 upstream. Problem: When IMA and VTPM are both enabled in kernel config, kernel hangs during bootup on LE OS. Why?: IMA calls tpm_pcr_read() which results in tpm_ibmvtpm_send and tpm_ibmtpm_recv getting called. A trace showed that tpm_ibmtpm_recv was hanging. Resolution: tpm_ibmtpm_recv was hanging because tpm_ibmvtpm_send was sending CRQ message that probably did not make much sense to phype because of Endianness. The fix below sends correctly converted CRQ for LE. This was not caught before because it seems IMA is not enabled by default in kernel config and IMA exercises this particular code path in vtpm. Tested with IMA and VTPM enabled in kernel config and VTPM enabled on both a BE OS and a LE OS ppc64 lpar. This exercised CRQ and TPM command code paths in vtpm. Patch is against Peter's tpmdd tree on github which included Vicky's previous vtpm le patches. Signed-off-by: Joy Latten Reviewed-by: Ashley Lai Signed-off-by: Peter Huewe Signed-off-by: Greg Kroah-Hartman --- drivers/char/tpm/tpm_ibmvtpm.c | 10 +++++----- drivers/char/tpm/tpm_ibmvtpm.h | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/char/tpm/tpm_ibmvtpm.c b/drivers/char/tpm/tpm_ibmvtpm.c index eff9d587003484..102463ba745dbb 100644 --- a/drivers/char/tpm/tpm_ibmvtpm.c +++ b/drivers/char/tpm/tpm_ibmvtpm.c @@ -124,7 +124,7 @@ static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) { struct ibmvtpm_dev *ibmvtpm; struct ibmvtpm_crq crq; - u64 *word = (u64 *) &crq; + __be64 *word = (__be64 *)&crq; int rc; ibmvtpm = (struct ibmvtpm_dev *)TPM_VPRIV(chip); @@ -145,11 +145,11 @@ static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) memcpy((void *)ibmvtpm->rtce_buf, (void *)buf, count); crq.valid = (u8)IBMVTPM_VALID_CMD; crq.msg = (u8)VTPM_TPM_COMMAND; - crq.len = (u16)count; - crq.data = ibmvtpm->rtce_dma_handle; + crq.len = cpu_to_be16(count); + crq.data = cpu_to_be32(ibmvtpm->rtce_dma_handle); - rc = ibmvtpm_send_crq(ibmvtpm->vdev, cpu_to_be64(word[0]), - cpu_to_be64(word[1])); + rc = ibmvtpm_send_crq(ibmvtpm->vdev, be64_to_cpu(word[0]), + be64_to_cpu(word[1])); if (rc != H_SUCCESS) { dev_err(ibmvtpm->dev, "tpm_ibmvtpm_send failed rc=%d\n", rc); rc = 0; diff --git a/drivers/char/tpm/tpm_ibmvtpm.h b/drivers/char/tpm/tpm_ibmvtpm.h index bd82a791f995d6..b2c231b1beec4d 100644 --- a/drivers/char/tpm/tpm_ibmvtpm.h +++ b/drivers/char/tpm/tpm_ibmvtpm.h @@ -22,9 +22,9 @@ struct ibmvtpm_crq { u8 valid; u8 msg; - u16 len; - u32 data; - u64 reserved; + __be16 len; + __be32 data; + __be64 reserved; } __attribute__((packed, aligned(8))); struct ibmvtpm_crq_queue { From a1e7c4d07aed633b28608db4d268c4a4db1e4d33 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Tue, 13 Jan 2015 23:13:14 +0100 Subject: [PATCH 417/788] tpm/tpm_i2c_stm_st33: Add status check when reading data on the FIFO commit c4eadfafb91d5501095c55ffadaa1168743f39d3 upstream. Add a return value check when reading data from the FIFO register. Reviewed-by: Jason Gunthorpe Signed-off-by: Christophe Ricard Reviewed-by: Peter Huewe Signed-off-by: Peter Huewe Signed-off-by: Greg Kroah-Hartman --- drivers/char/tpm/tpm_i2c_stm_st33.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/char/tpm/tpm_i2c_stm_st33.c b/drivers/char/tpm/tpm_i2c_stm_st33.c index 7d1c540fa26a89..3f187a529e9299 100644 --- a/drivers/char/tpm/tpm_i2c_stm_st33.c +++ b/drivers/char/tpm/tpm_i2c_stm_st33.c @@ -397,7 +397,7 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, */ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) { - int size = 0, burstcnt, len; + int size = 0, burstcnt, len, ret; struct i2c_client *client; client = (struct i2c_client *)TPM_VPRIV(chip); @@ -406,13 +406,15 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) wait_for_stat(chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID, chip->vendor.timeout_c, - &chip->vendor.read_queue) - == 0) { + &chip->vendor.read_queue) == 0) { burstcnt = get_burstcount(chip); if (burstcnt < 0) return burstcnt; len = min_t(int, burstcnt, count - size); - I2C_READ_DATA(client, TPM_DATA_FIFO, buf + size, len); + ret = I2C_READ_DATA(client, TPM_DATA_FIFO, buf + size, len); + if (ret < 0) + return ret; + size += len; } return size; From 5bbd2846646689008d534b0a68178956dc70a579 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Wed, 25 Feb 2015 13:17:48 +0100 Subject: [PATCH 418/788] s390/pci: fix possible information leak in mmio syscall commit f0483044c1c96089256cda4cf182eea1ead77fe4 upstream. Make sure that even in error situations we do not use copy_to_user on uninitialized kernel memory. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky Signed-off-by: Greg Kroah-Hartman --- arch/s390/pci/pci_mmio.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/arch/s390/pci/pci_mmio.c b/arch/s390/pci/pci_mmio.c index 62c5ea6d8682a5..ec86d4f22efdf1 100644 --- a/arch/s390/pci/pci_mmio.c +++ b/arch/s390/pci/pci_mmio.c @@ -64,8 +64,7 @@ SYSCALL_DEFINE3(s390_pci_mmio_write, unsigned long, mmio_addr, if (copy_from_user(buf, user_buffer, length)) goto out; - memcpy_toio(io_addr, buf, length); - ret = 0; + ret = zpci_memcpy_toio(io_addr, buf, length); out: if (buf != local_buf) kfree(buf); @@ -98,16 +97,16 @@ SYSCALL_DEFINE3(s390_pci_mmio_read, unsigned long, mmio_addr, goto out; io_addr = (void *)((pfn << PAGE_SHIFT) | (mmio_addr & ~PAGE_MASK)); - ret = -EFAULT; - if ((unsigned long) io_addr < ZPCI_IOMAP_ADDR_BASE) + if ((unsigned long) io_addr < ZPCI_IOMAP_ADDR_BASE) { + ret = -EFAULT; goto out; - - memcpy_fromio(buf, io_addr, length); - - if (copy_to_user(user_buffer, buf, length)) + } + ret = zpci_memcpy_fromio(buf, io_addr, length); + if (ret) goto out; + if (copy_to_user(user_buffer, buf, length)) + ret = -EFAULT; - ret = 0; out: if (buf != local_buf) kfree(buf); From 5d581084067d7f5d16a73210d2219ac711a8a987 Mon Sep 17 00:00:00 2001 From: Torsten Fleischer Date: Tue, 24 Feb 2015 16:32:57 +0100 Subject: [PATCH 419/788] spi: atmel: Fix interrupt setup for PDC transfers commit 76e1d14b316d6f501ebc001e7a5d86b24ce5b615 upstream. Additionally to the current DMA transfer the PDC allows to set up a next DMA transfer. This is useful for larger SPI transfers. The driver currently waits for ENDRX as end of the transfer. But ENDRX is set when the current DMA transfer is done (RCR = 0), i.e. it doesn't include the next DMA transfer. Thus a subsequent SPI transfer could be started although there is currently a transfer in progress. This can cause invalid accesses to the SPI slave devices and to SPI transfer errors. This issue has been observed on a hardware with a M25P128 SPI NOR flash. So instead of ENDRX we should wait for RXBUFF. This flag is set if there is no more DMA transfer in progress (RCR = RNCR = 0). Signed-off-by: Torsten Fleischer Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/spi/spi-atmel.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 23d8f5f56579a8..df93c9700c2e5a 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -764,17 +764,17 @@ static void atmel_spi_pdc_next_xfer(struct spi_master *master, (unsigned long long)xfer->rx_dma); } - /* REVISIT: We're waiting for ENDRX before we start the next + /* REVISIT: We're waiting for RXBUFF before we start the next * transfer because we need to handle some difficult timing - * issues otherwise. If we wait for ENDTX in one transfer and - * then starts waiting for ENDRX in the next, it's difficult - * to tell the difference between the ENDRX interrupt we're - * actually waiting for and the ENDRX interrupt of the + * issues otherwise. If we wait for TXBUFE in one transfer and + * then starts waiting for RXBUFF in the next, it's difficult + * to tell the difference between the RXBUFF interrupt we're + * actually waiting for and the RXBUFF interrupt of the * previous transfer. * * It should be doable, though. Just not now... */ - spi_writel(as, IER, SPI_BIT(ENDRX) | SPI_BIT(OVRES)); + spi_writel(as, IER, SPI_BIT(RXBUFF) | SPI_BIT(OVRES)); spi_writel(as, PTCR, SPI_BIT(TXTEN) | SPI_BIT(RXTEN)); } From 218e39d05f324e27347be8f33338651aa7bc7a19 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 2 Mar 2015 20:15:58 +0200 Subject: [PATCH 420/788] spi: dw-mid: avoid potential NULL dereference commit c9dafb27c84412fe4b17c3b94cc4ffeef5df1833 upstream. When DMA descriptor allocation fails we should not try to assign any fields in the bad descriptor. The patch adds the necessary checks for that. Fixes: 7063c0d942a1 (spi/dw_spi: add DMA support) Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/spi/spi-dw-mid.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index a67d37c7e3c00f..22ca08a18b9e73 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -139,6 +139,9 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws) 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!txdesc) + return NULL; + txdesc->callback = dw_spi_dma_tx_done; txdesc->callback_param = dws; @@ -184,6 +187,9 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws) 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!rxdesc) + return NULL; + rxdesc->callback = dw_spi_dma_rx_done; rxdesc->callback_param = dws; From f1cb26ab0c3638a6ebdd1a56ab576fb14ba19edd Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Fri, 27 Feb 2015 16:30:21 +0100 Subject: [PATCH 421/788] spi: pl022: Fix race in giveback() leading to driver lock-up commit cd6fa8d2ca53cac3226fdcffcf763be390abae32 upstream. Commit fd316941c ("spi/pl022: disable port when unused") introduced a race, which leads to possible driver lock up (easily reproducible on SMP). The problem happens in giveback() function where the completion of the transfer is signalled to SPI subsystem and then the HW SPI controller is disabled. Another transfer might be setup in between, which brings driver in locked-up state. Exact event sequence on SMP: core0 core1 => pump_transfers() /* message->state == STATE_DONE */ => giveback() => spi_finalize_current_message() => pl022_unprepare_transfer_hardware() => pl022_transfer_one_message => flush() => do_interrupt_dma_transfer() => set_up_next_transfer() /* Enable SSP, turn on interrupts */ writew((readw(SSP_CR1(pl022->virtbase)) | SSP_CR1_MASK_SSE), SSP_CR1(pl022->virtbase)); ... => pl022_interrupt_handler() => readwriter() /* disable the SPI/SSP operation */ => writew((readw(SSP_CR1(pl022->virtbase)) & (~SSP_CR1_MASK_SSE)), SSP_CR1(pl022->virtbase)); Lockup! SPI controller is disabled and the data will never be received. Whole SPI subsystem is waiting for transfer ACK and blocked. So, only signal transfer completion after disabling the controller. Fixes: fd316941c (spi/pl022: disable port when unused) Signed-off-by: Alexander Sverdlin Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/spi/spi-pl022.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 89ca162801da10..ee513a85296b19 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -534,12 +534,12 @@ static void giveback(struct pl022 *pl022) pl022->cur_msg = NULL; pl022->cur_transfer = NULL; pl022->cur_chip = NULL; - spi_finalize_current_message(pl022->master); /* disable the SPI/SSP operation */ writew((readw(SSP_CR1(pl022->virtbase)) & (~SSP_CR1_MASK_SSE)), SSP_CR1(pl022->virtbase)); + spi_finalize_current_message(pl022->master); } /** From 2838a548aac0ea452e1e5dd2983c9b126726e986 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 12 Mar 2015 09:41:32 +0100 Subject: [PATCH 422/788] ALSA: snd-usb: add quirks for Roland UA-22 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit fcdcd1dec6d2c7b718385ec743ae5a9a233edad4 upstream. The device complies to the UAC1 standard but hides that fact with proprietary descriptors. The autodetect quirk for Roland devices catches the audio interface but misses the MIDI part, so a specific quirk is needed. Signed-off-by: Daniel Mack Reported-by: Rafa Lafuente Tested-by: Raphaël Doursenaud Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/quirks-table.h | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 0a598af9b38b81..e61c167da72c80 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -1773,6 +1773,36 @@ YAMAHA_DEVICE(0x7010, "UB99"), } } }, +{ + USB_DEVICE(0x0582, 0x0159), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + /* .vendor_name = "Roland", */ + /* .product_name = "UA-22", */ + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + }, + { + .ifnum = -1 + } + } + } +}, /* this catches most recent vendor-specific Roland devices */ { .match_flags = USB_DEVICE_ID_MATCH_VENDOR | From 7f582e8a47360fe30fb17100f9a5e272285831dc Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 11 Mar 2015 18:12:49 +0100 Subject: [PATCH 423/788] ALSA: control: Add sanity checks for user ctl id name string commit be3bb8236db2d0fcd705062ae2e2a9d75131222f upstream. There was no check about the id string of user control elements, so we accepted even a control element with an empty string, which is obviously bogus. This patch adds more sanity checks of id strings. Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/control.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/core/control.c b/sound/core/control.c index bb96a467e88d08..23b018b717b019 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -1168,6 +1168,10 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, if (info->count < 1) return -EINVAL; + if (!*info->id.name) + return -EINVAL; + if (strnlen(info->id.name, sizeof(info->id.name)) >= sizeof(info->id.name)) + return -EINVAL; access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : (info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE| SNDRV_CTL_ELEM_ACCESS_INACTIVE| From 0bf939beaf7c22a2c72caf5e92482e016b286c01 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 11 Mar 2015 16:05:19 +0100 Subject: [PATCH 424/788] ALSA: hda - Fix built-in mic on Compaq Presario CQ60 commit ddb6ca75b5671b8fbf1909bc588c449ee74b34f9 upstream. Compaq Presario CQ60 laptop with CX20561 gives a wrong pin for the built-in mic NID 0x17 instead of NID 0x1d, and it results in the non-working mic. This patch just remaps the pin correctly via fixup. Bugzilla: https://bugzilla.opensuse.org/show_bug.cgi?id=920604 Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_conexant.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index fd3ed18670e9c4..da67ea8645a6e8 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -223,6 +223,7 @@ enum { CXT_PINCFG_LENOVO_TP410, CXT_PINCFG_LEMOTE_A1004, CXT_PINCFG_LEMOTE_A1205, + CXT_PINCFG_COMPAQ_CQ60, CXT_FIXUP_STEREO_DMIC, CXT_FIXUP_INC_MIC_BOOST, CXT_FIXUP_HEADPHONE_MIC_PIN, @@ -660,6 +661,15 @@ static const struct hda_fixup cxt_fixups[] = { .type = HDA_FIXUP_PINS, .v.pins = cxt_pincfg_lemote, }, + [CXT_PINCFG_COMPAQ_CQ60] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + /* 0x17 was falsely set up as a mic, it should 0x1d */ + { 0x17, 0x400001f0 }, + { 0x1d, 0x97a70120 }, + { } + } + }, [CXT_FIXUP_STEREO_DMIC] = { .type = HDA_FIXUP_FUNC, .v.func = cxt_fixup_stereo_dmic, @@ -769,6 +779,7 @@ static const struct hda_model_fixup cxt5047_fixup_models[] = { }; static const struct snd_pci_quirk cxt5051_fixups[] = { + SND_PCI_QUIRK(0x103c, 0x360b, "Compaq CQ60", CXT_PINCFG_COMPAQ_CQ60), SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT_PINCFG_LENOVO_X200), {} }; From 7ebbfe77b02be3aec059a1c4f0f0c3a6fff0c8b6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 12 Mar 2015 08:30:11 +0100 Subject: [PATCH 425/788] ALSA: hda - Don't access stereo amps for mono channel widgets commit ef403edb75580a3ec5d155f5de82155f0419c621 upstream. The current HDA generic parser initializes / modifies the amp values always in stereo, but this seems causing the problem on ALC3229 codec that has a few mono channel widgets: namely, these mono widgets react to actions for both channels equally. In the driver code, we do care the mono channel and create a control only for the left channel (as defined in HD-audio spec) for such a node. When the control is updated, only the left channel value is changed. However, in the resume, the right channel value is also restored from the initial value we took as stereo, and this overwrites the left channel value. This ends up being the silent output as the right channel has been never touched and remains muted. This patch covers the places where unconditional stereo amp accesses are done and converts to the conditional accesses. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=94581 Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/hda_generic.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index b680b4ec63313c..fe18071bf93aab 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -692,7 +692,23 @@ static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx) { unsigned int caps = query_amp_caps(codec, nid, dir); int val = get_amp_val_to_activate(codec, nid, dir, caps, false); - snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val); + + if (get_wcaps(codec, nid) & AC_WCAP_STEREO) + snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val); + else + snd_hda_codec_amp_init(codec, nid, 0, dir, idx, 0xff, val); +} + +/* update the amp, doing in stereo or mono depending on NID */ +static int update_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx, + unsigned int mask, unsigned int val) +{ + if (get_wcaps(codec, nid) & AC_WCAP_STEREO) + return snd_hda_codec_amp_stereo(codec, nid, dir, idx, + mask, val); + else + return snd_hda_codec_amp_update(codec, nid, 0, dir, idx, + mask, val); } /* calculate amp value mask we can modify; @@ -732,7 +748,7 @@ static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir, return; val &= mask; - snd_hda_codec_amp_stereo(codec, nid, dir, idx, mask, val); + update_amp(codec, nid, dir, idx, mask, val); } static void activate_amp_out(struct hda_codec *codec, struct nid_path *path, @@ -4424,13 +4440,11 @@ static void mute_all_mixer_nid(struct hda_codec *codec, hda_nid_t mix) has_amp = nid_has_mute(codec, mix, HDA_INPUT); for (i = 0; i < nums; i++) { if (has_amp) - snd_hda_codec_amp_stereo(codec, mix, - HDA_INPUT, i, - 0xff, HDA_AMP_MUTE); + update_amp(codec, mix, HDA_INPUT, i, + 0xff, HDA_AMP_MUTE); else if (nid_has_volume(codec, conn[i], HDA_OUTPUT)) - snd_hda_codec_amp_stereo(codec, conn[i], - HDA_OUTPUT, 0, - 0xff, HDA_AMP_MUTE); + update_amp(codec, conn[i], HDA_OUTPUT, 0, + 0xff, HDA_AMP_MUTE); } } From 3718eb979b46bfc8fad21451fa7c2dd6a244d536 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 12 Mar 2015 20:28:04 +0100 Subject: [PATCH 426/788] ALSA: hda - Set single_adc_amp flag for CS420x codecs commit bad994f5b4ab57eec8d56c180edca00505c3eeb2 upstream. CS420x codecs seem to deal only the single amps of ADC nodes even though the nodes receive multiple inputs. This leads to the inconsistent amp value after S3/S4 resume, for example. The fix is just to set codec->single_adc_amp flag. Then the driver handles these ADC amps as if single connections. Reported-and-tested-by: Vasil Zlatanov Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_cirrus.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 1589c9bcce3e15..ab687ffb28c22c 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -584,6 +584,7 @@ static int patch_cs420x(struct hda_codec *codec) return -ENOMEM; spec->gen.automute_hook = cs_automute; + codec->single_adc_amp = 1; snd_hda_pick_fixup(codec, cs420x_models, cs420x_fixup_tbl, cs420x_fixups); From c8bdcd4cb80bd033661f349824b0a9ecac133620 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 12 Mar 2015 20:47:15 +0100 Subject: [PATCH 427/788] ALSA: hda - Add workaround for MacBook Air 5,2 built-in mic commit 2ddee91abe9cc34ddb6294ee14702b46ae07d460 upstream. MacBook Air 5,2 has the same problem as MacBook Pro 8,1 where the built-in mic records only the right channel. Apply the same workaround as MBP8,1 to spread the mono channel via a Cirrus codec vendor-specific COEF setup. Reported-and-tested-by: Vasil Zlatanov Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_cirrus.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index ab687ffb28c22c..dd2b3d92071f69 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -393,6 +393,7 @@ static const struct snd_pci_quirk cs420x_fixup_tbl[] = { SND_PCI_QUIRK(0x106b, 0x1c00, "MacBookPro 8,1", CS420X_MBP81), SND_PCI_QUIRK(0x106b, 0x2000, "iMac 12,2", CS420X_IMAC27_122), SND_PCI_QUIRK(0x106b, 0x2800, "MacBookPro 10,1", CS420X_MBP101), + SND_PCI_QUIRK(0x106b, 0x5600, "MacBookAir 5,2", CS420X_MBP81), SND_PCI_QUIRK(0x106b, 0x5b00, "MacBookAir 4,2", CS420X_MBA42), SND_PCI_QUIRK_VENDOR(0x106b, "Apple", CS420X_APPLE), {} /* terminator */ From 6bbe2beabcbb62045a944eb1adf8cc14fec5b5ff Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 8 Mar 2015 18:29:50 +0100 Subject: [PATCH 428/788] ALSA: hda - Fix regression of HD-audio controller fallback modes commit a1f3f1ca66bd12c339b17a0c2ef93a093f90a277 upstream. The commit [63e51fd708f5: ALSA: hda - Don't take unresponsive D3 transition too serious] introduced a conditional fallback behavior to the HD-audio controller depending on the flag set. However, it introduced a silly bug, too, that the flag was evaluated in a reverse way. This resulted in a regression of HD-audio controller driver where it can't go to the fallback mode at communication errors. Unfortunately (or fortunately?) this didn't come up until recently because the affected code path is an error handling that happens only on an unstable hardware chip. Most of recent chips work stably, thus they didn't hit this problem. Now, we've got a regression report with a VIA chip, and this seems indeed requiring the fallback to the polling mode, and finally the bug was revealed. The fix is a oneliner to remove the wrong logical NOT in the check. (Lesson learned - be careful about double negation.) The bug should be backported to stable, but the patch won't be applicable to 3.13 or earlier because of the code splits. The stable fix patches for earlier kernels will be posted later manually. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=94021 Fixes: 63e51fd708f5 ('ALSA: hda - Don't take unresponsive D3 transition too serious') Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/hda_controller.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index c2aa3cd844e58a..a9536bb0dd73b5 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -1160,7 +1160,7 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus, } } - if (!bus->no_response_fallback) + if (bus->no_response_fallback) return -1; if (!chip->polling_mode && chip->poll_count < 2) { From 03fa7bb8c85d7311e9c4798e468b998bdd965151 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 16 Mar 2015 10:18:08 +0100 Subject: [PATCH 429/788] ALSA: hda - Treat stereo-to-mono mix properly commit cc261738add93947d138d2fabad9f4dbed4e5c00 upstream. The commit [ef403edb7558: ALSA: hda - Don't access stereo amps for mono channel widgets] fixed the handling of mono widgets in general, but it still misses an exceptional case: namely, a mono mixer widget taking a single stereo input. In this case, it has stereo volumes although it's a mono widget, and thus we have to take care of both left and right input channels, as stated in HD-audio spec ("7.1.3 Widget Interconnection Rules"). This patch covers this missing piece by adding proper checks of stereo amps in both the generic parser and the proc output codes. Reported-by: Raymond Yau Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/hda_generic.c | 21 ++++++++++++++++++-- sound/pci/hda/hda_proc.c | 38 +++++++++++++++++++++++++++++-------- 2 files changed, 49 insertions(+), 10 deletions(-) diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index fe18071bf93aab..8ec5289f8e0585 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -687,13 +687,30 @@ static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid, return val; } +/* is this a stereo widget or a stereo-to-mono mix? */ +static bool is_stereo_amps(struct hda_codec *codec, hda_nid_t nid, int dir) +{ + unsigned int wcaps = get_wcaps(codec, nid); + hda_nid_t conn; + + if (wcaps & AC_WCAP_STEREO) + return true; + if (dir != HDA_INPUT || get_wcaps_type(wcaps) != AC_WID_AUD_MIX) + return false; + if (snd_hda_get_num_conns(codec, nid) != 1) + return false; + if (snd_hda_get_connections(codec, nid, &conn, 1) < 0) + return false; + return !!(get_wcaps(codec, conn) & AC_WCAP_STEREO); +} + /* initialize the amp value (only at the first time) */ static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx) { unsigned int caps = query_amp_caps(codec, nid, dir); int val = get_amp_val_to_activate(codec, nid, dir, caps, false); - if (get_wcaps(codec, nid) & AC_WCAP_STEREO) + if (is_stereo_amps(codec, nid, dir)) snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val); else snd_hda_codec_amp_init(codec, nid, 0, dir, idx, 0xff, val); @@ -703,7 +720,7 @@ static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx) static int update_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx, unsigned int mask, unsigned int val) { - if (get_wcaps(codec, nid) & AC_WCAP_STEREO) + if (is_stereo_amps(codec, nid, dir)) return snd_hda_codec_amp_stereo(codec, nid, dir, idx, mask, val); else diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index ce5a6da834199b..05e19f78b4cb86 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -134,13 +134,38 @@ static void print_amp_caps(struct snd_info_buffer *buffer, (caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT); } +/* is this a stereo widget or a stereo-to-mono mix? */ +static bool is_stereo_amps(struct hda_codec *codec, hda_nid_t nid, + int dir, unsigned int wcaps, int indices) +{ + hda_nid_t conn; + + if (wcaps & AC_WCAP_STEREO) + return true; + /* check for a stereo-to-mono mix; it must be: + * only a single connection, only for input, and only a mixer widget + */ + if (indices != 1 || dir != HDA_INPUT || + get_wcaps_type(wcaps) != AC_WID_AUD_MIX) + return false; + + if (snd_hda_get_raw_connections(codec, nid, &conn, 1) < 0) + return false; + /* the connection source is a stereo? */ + wcaps = snd_hda_param_read(codec, conn, AC_PAR_AUDIO_WIDGET_CAP); + return !!(wcaps & AC_WCAP_STEREO); +} + static void print_amp_vals(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid, - int dir, int stereo, int indices) + int dir, unsigned int wcaps, int indices) { unsigned int val; + bool stereo; int i; + stereo = is_stereo_amps(codec, nid, dir, wcaps, indices); + dir = dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT; for (i = 0; i < indices; i++) { snd_iprintf(buffer, " ["); @@ -757,12 +782,10 @@ static void print_codec_info(struct snd_info_entry *entry, (codec->single_adc_amp && wid_type == AC_WID_AUD_IN)) print_amp_vals(buffer, codec, nid, HDA_INPUT, - wid_caps & AC_WCAP_STEREO, - 1); + wid_caps, 1); else print_amp_vals(buffer, codec, nid, HDA_INPUT, - wid_caps & AC_WCAP_STEREO, - conn_len); + wid_caps, conn_len); } if (wid_caps & AC_WCAP_OUT_AMP) { snd_iprintf(buffer, " Amp-Out caps: "); @@ -771,11 +794,10 @@ static void print_codec_info(struct snd_info_entry *entry, if (wid_type == AC_WID_PIN && codec->pin_amp_workaround) print_amp_vals(buffer, codec, nid, HDA_OUTPUT, - wid_caps & AC_WCAP_STEREO, - conn_len); + wid_caps, conn_len); else print_amp_vals(buffer, codec, nid, HDA_OUTPUT, - wid_caps & AC_WCAP_STEREO, 1); + wid_caps, 1); } switch (wid_type) { From e93d9bd175bb768183af54d6227265e007bcf9b6 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 18 Feb 2015 11:32:07 +0100 Subject: [PATCH 430/788] mtd: nand: pxa3xx: Fix PIO FIFO draining commit 8dad0386b97c4bd6edd56752ca7f2e735fe5beb4 upstream. The NDDB register holds the data that are needed by the read and write commands. However, during a read PIO access, the datasheet specifies that after each 32 bytes read in that register, when BCH is enabled, we have to make sure that the RDDREQ bit is set in the NDSR register. This fixes an issue that was seen on the Armada 385, and presumably other mvebu SoCs, when a read on a newly erased page would end up in the driver reporting a timeout from the NAND. Signed-off-by: Maxime Ripard Reviewed-by: Boris Brezillon Acked-by: Ezequiel Garcia Signed-off-by: Brian Norris Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/nand/pxa3xx_nand.c | 48 +++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 96b0b1d27df1b2..bc677362bc73d5 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -480,6 +480,42 @@ static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) nand_writel(info, NDCR, ndcr | int_mask); } +static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len) +{ + if (info->ecc_bch) { + int timeout; + + /* + * According to the datasheet, when reading from NDDB + * with BCH enabled, after each 32 bytes reads, we + * have to make sure that the NDSR.RDDREQ bit is set. + * + * Drain the FIFO 8 32 bits reads at a time, and skip + * the polling on the last read. + */ + while (len > 8) { + __raw_readsl(info->mmio_base + NDDB, data, 8); + + for (timeout = 0; + !(nand_readl(info, NDSR) & NDSR_RDDREQ); + timeout++) { + if (timeout >= 5) { + dev_err(&info->pdev->dev, + "Timeout on RDDREQ while draining the FIFO\n"); + return; + } + + mdelay(1); + } + + data += 32; + len -= 8; + } + } + + __raw_readsl(info->mmio_base + NDDB, data, len); +} + static void handle_data_pio(struct pxa3xx_nand_info *info) { unsigned int do_bytes = min(info->data_size, info->chunk_size); @@ -496,14 +532,14 @@ static void handle_data_pio(struct pxa3xx_nand_info *info) DIV_ROUND_UP(info->oob_size, 4)); break; case STATE_PIO_READING: - __raw_readsl(info->mmio_base + NDDB, - info->data_buff + info->data_buff_pos, - DIV_ROUND_UP(do_bytes, 4)); + drain_fifo(info, + info->data_buff + info->data_buff_pos, + DIV_ROUND_UP(do_bytes, 4)); if (info->oob_size > 0) - __raw_readsl(info->mmio_base + NDDB, - info->oob_buff + info->oob_buff_pos, - DIV_ROUND_UP(info->oob_size, 4)); + drain_fifo(info, + info->oob_buff + info->oob_buff_pos, + DIV_ROUND_UP(info->oob_size, 4)); break; default: dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__, From 4f500c93b2cd2d1c81883039131c6077c933474c Mon Sep 17 00:00:00 2001 From: Brian King Date: Wed, 4 Mar 2015 08:09:44 -0600 Subject: [PATCH 431/788] bnx2x: Force fundamental reset for EEH recovery commit da293700568ed3d96fcf062ac15d7d7c41377f11 upstream. EEH recovery for bnx2x based adapters is not reliable on all Power systems using the default hot reset, which can result in an unrecoverable EEH error. Forcing the use of fundamental reset during EEH recovery fixes this. Signed-off-by: Brian King Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 72eef9fc883e89..ac6a0ef44e1366 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -12722,6 +12722,9 @@ static int bnx2x_init_dev(struct bnx2x *bp, struct pci_dev *pdev, pci_write_config_dword(bp->pdev, PCICFG_GRC_ADDRESS, PCICFG_VENDOR_ID_OFFSET); + /* Set PCIe reset type to fundamental for EEH recovery */ + pdev->needs_freset = 1; + /* AER (Advanced Error reporting) configuration */ rc = pci_enable_pcie_error_reporting(pdev); if (!rc) From 8b86cf382cada10567345a558e38911f179f3c1a Mon Sep 17 00:00:00 2001 From: Fugang Duan Date: Wed, 4 Mar 2015 07:52:11 +0800 Subject: [PATCH 432/788] net: fec: fix rcv is not last issue when do suspend/resume test commit 61615cd27e2fdcf698261ba77c7d93f7a7739c65 upstream. When do suspend/resume stress test, some log shows "rcv is not +last". The issue is that enet suspend will disable phy clock, phy link down, after resume back, enet MAC redo initial and ready to tx/rx packet, but phy still is not ready which is doing auto-negotiation. When phy link is not up, don't schdule napi soft irq. [Peter] It has fixed kernel panic after long time suspend/resume test with nfs rootfs. [ 8864.429458] fec 2188000.ethernet eth0: rcv is not +last [ 8864.434799] fec 2188000.ethernet eth0: rcv is not +last [ 8864.440088] fec 2188000.ethernet eth0: rcv is not +last [ 8864.445424] fec 2188000.ethernet eth0: rcv is not +last [ 8864.450782] fec 2188000.ethernet eth0: rcv is not +last [ 8864.456111] Unable to handle kernel NULL pointer dereference at virtual address 00000000 [ 8864.464225] pgd = 80004000 [ 8864.466997] [00000000] *pgd=00000000 [ 8864.470627] Internal error: Oops: 17 [#1] SMP ARM [ 8864.475353] Modules linked in: evbug [ 8864.479006] CPU: 0 PID: 3 Comm: ksoftirqd/0 Not tainted 4.0.0-rc1-00044-g7a2a1d2 #234 [ 8864.486854] Hardware name: Freescale i.MX6 SoloX (Device Tree) [ 8864.492709] task: be069380 ti: be07a000 task.ti: be07a000 [ 8864.498137] PC is at memcpy+0x80/0x330 [ 8864.501919] LR is at gro_pull_from_frag0+0x34/0xa8 [ 8864.506735] pc : [<802bb080>] lr : [<8057c204>] psr: 00000113 [ 8864.506735] sp : be07bbd4 ip : 00000010 fp : be07bc0c [ 8864.518235] r10: 0000000e r9 : 00000000 r8 : 809c7754 [ 8864.523479] r7 : 809c7754 r6 : bb43c040 r5 : bd280cc0 r4 : 00000012 [ 8864.530025] r3 : 00000804 r2 : fffffff2 r1 : 00000000 r0 : bb43b83c [ 8864.536575] Flags: nzcv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel [ 8864.543904] Control: 10c5387d Table: bd14c04a DAC: 00000015 [ 8864.549669] Process ksoftirqd/0 (pid: 3, stack limit = 0xbe07a210) [ 8864.555869] Stack: (0xbe07bbd4 to 0xbe07c000) [ 8864.560250] bbc0: bd280cc0 bb43c040 809c7754 [ 8864.568455] bbe0: 809c7754 bb43b83c 00000012 8057c204 00000000 bd280cc0 bd8a0718 00000003 [ 8864.576658] bc00: be07bc5c be07bc10 8057ebf0 8057c1dc 00000000 00000000 8057ecc4 bef59760 [ 8864.584863] bc20: 00000002 bd8a0000 be07bc64 809c7754 00000000 bd8a0718 bd280cc0 bd8a0000 [ 8864.593066] bc40: 00000000 0000001c 00000000 bd8a0000 be07bc74 be07bc60 8057f148 8057eb90 [ 8864.601268] bc60: bf0810a0 00000000 be07bcf4 be07bc78 8044e7b4 8057f12c 00000000 8007df6c [ 8864.609470] bc80: bd8a0718 00000040 00000000 bd280a80 00000002 00000019 bd8a0600 bd8a1214 [ 8864.617672] bca0: bd8a0690 bf0810a0 00000000 00000000 bd8a1000 00000000 00000027 bd280cc0 [ 8864.625874] bcc0: 80062708 800625cc 000943db bd8a0718 00000001 000d1166 00000040 be7c1ec0 [ 8864.634077] bce0: 0000012c be07bd00 be07bd3c be07bcf8 8057fc98 8044e3ac 809c2ec0 3ddff000 [ 8864.642280] bd00: be07bd00 be07bd00 be07bd08 be07bd08 00000000 00000020 809c608c 00000003 [ 8864.650481] bd20: 809c6080 40000001 809c6088 00200100 be07bd84 be07bd40 8002e690 8057fac8 [ 8864.658684] bd40: be07bd64 be07bd50 00000001 04208040 000d1165 0000000a be07bd84 809c0d7c [ 8864.666885] bd60: 00000000 809c6af8 00000000 00000001 be008000 00000000 be07bd9c be07bd88 [ 8864.675087] bd80: 8002eb64 8002e564 00000125 809c0d7c be07bdc4 be07bda0 8006f100 8002eaac [ 8864.683291] bda0: c080e10c be07bde8 809c6c6c c080e100 00000002 00000000 be07bde4 be07bdc8 [ 8864.691492] bdc0: 800087a0 8006f098 806f2934 20000013 ffffffff be07be1c be07be44 be07bde8 [ 8864.699695] bde0: 800133a4 80008784 00000001 00000001 00000000 00000000 be7c1680 00000000 [ 8864.707896] be00: be0cfe00 bd93eb40 00000002 00000000 00000000 be07be44 be07be00 be07be30 [ 8864.716098] be20: 8006278c 806f2934 20000013 ffffffff be069380 be7c1680 be07be7c be07be48 [ 8864.724300] be40: 80049cfc 806f2910 00000001 00000000 80049cb4 00000000 be07be7c be7c1680 [ 8864.732502] be60: be3289c0 be069380 bd23b600 be0cfe00 be07bebc be07be80 806ed614 80049c68 [ 8864.740706] be80: be07a000 0000020a 809c608c 00000003 00000001 8002e858 be07a000 be035740 [ 8864.748907] bea0: 00000000 00000001 809d4598 00000000 be07bed4 be07bec0 806edd0c 806ed440 [ 8864.757110] bec0: be07a000 be07a000 be07bee4 be07bed8 806edd68 806edcf0 be07bef4 be07bee8 [ 8864.765311] bee0: 8002e860 806edd34 be07bf24 be07bef8 800494b0 8002e828 be069380 00000000 [ 8864.773512] bf00: be035780 be035740 8004938c 00000000 00000000 00000000 be07bfac be07bf28 [ 8864.781715] bf20: 80045928 80049398 be07bf44 00000001 00000000 be035740 00000000 00030003 [ 8864.789917] bf40: dead4ead ffffffff ffffffff 80a2716c 80b59b00 00000000 8088c954 be07bf5c [ 8864.798120] bf60: be07bf5c 00000000 00000000 dead4ead ffffffff ffffffff 80a2716c 00000000 [ 8864.806320] bf80: 00000000 8088c954 be07bf88 be07bf88 be035780 8004584c 00000000 00000000 [ 8864.814523] bfa0: 00000000 be07bfb0 8000ed10 80045858 00000000 00000000 00000000 00000000 [ 8864.822723] bfc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 [ 8864.830925] bfe0: 00000000 00000000 00000000 00000000 00000013 00000000 5ffbb5f7 f9fcf5e7 [ 8864.839115] Backtrace: [ 8864.841631] [<8057c1d0>] (gro_pull_from_frag0) from [<8057ebf0>] (dev_gro_receive+0x6c/0x3f8) [ 8864.850173] r6:00000003 r5:bd8a0718 r4:bd280cc0 r3:00000000 [ 8864.855958] [<8057eb84>] (dev_gro_receive) from [<8057f148>] (napi_gro_receive+0x28/0xac) [ 8864.864152] r10:bd8a0000 r9:00000000 r8:0000001c r7:00000000 r6:bd8a0000 r5:bd280cc0 [ 8864.872115] r4:bd8a0718 [ 8864.874713] [<8057f120>] (napi_gro_receive) from [<8044e7b4>] (fec_enet_rx_napi+0x414/0xc74) [ 8864.883167] r5:00000000 r4:bf0810a0 [ 8864.886823] [<8044e3a0>] (fec_enet_rx_napi) from [<8057fc98>] (net_rx_action+0x1dc/0x2ec) [ 8864.895016] r10:be07bd00 r9:0000012c r8:be7c1ec0 r7:00000040 r6:000d1166 r5:00000001 [ 8864.902982] r4:bd8a0718 [ 8864.905570] [<8057fabc>] (net_rx_action) from [<8002e690>] (__do_softirq+0x138/0x2c4) [ 8864.913417] r10:00200100 r9:809c6088 r8:40000001 r7:809c6080 r6:00000003 r5:809c608c [ 8864.921382] r4:00000020 [ 8864.923966] [<8002e558>] (__do_softirq) from [<8002eb64>] (irq_exit+0xc4/0x138) [ 8864.931289] r10:00000000 r9:be008000 r8:00000001 r7:00000000 r6:809c6af8 r5:00000000 [ 8864.939252] r4:809c0d7c [ 8864.941841] [<8002eaa0>] (irq_exit) from [<8006f100>] (__handle_domain_irq+0x74/0xe8) [ 8864.949688] r4:809c0d7c r3:00000125 [ 8864.953342] [<8006f08c>] (__handle_domain_irq) from [<800087a0>] (gic_handle_irq+0x28/0x68) [ 8864.961707] r9:00000000 r8:00000002 r7:c080e100 r6:809c6c6c r5:be07bde8 r4:c080e10c [ 8864.969597] [<80008778>] (gic_handle_irq) from [<800133a4>] (__irq_svc+0x44/0x5c) [ 8864.977097] Exception stack(0xbe07bde8 to 0xbe07be30) [ 8864.982173] bde0: 00000001 00000001 00000000 00000000 be7c1680 00000000 [ 8864.990377] be00: be0cfe00 bd93eb40 00000002 00000000 00000000 be07be44 be07be00 be07be30 [ 8864.998573] be20: 8006278c 806f2934 20000013 ffffffff [ 8865.003638] r7:be07be1c r6:ffffffff r5:20000013 r4:806f2934 [ 8865.009447] [<806f2904>] (_raw_spin_unlock_irq) from [<80049cfc>] (finish_task_switch+0xa0/0x160) [ 8865.018334] r4:be7c1680 r3:be069380 [ 8865.021993] [<80049c5c>] (finish_task_switch) from [<806ed614>] (__schedule+0x1e0/0x5dc) [ 8865.030098] r8:be0cfe00 r7:bd23b600 r6:be069380 r5:be3289c0 r4:be7c1680 [ 8865.036942] [<806ed434>] (__schedule) from [<806edd0c>] (preempt_schedule_common+0x28/0x44) [ 8865.045307] r9:00000000 r8:809d4598 r7:00000001 r6:00000000 r5:be035740 r4:be07a000 [ 8865.053197] [<806edce4>] (preempt_schedule_common) from [<806edd68>] (_cond_resched+0x40/0x48) [ 8865.061822] r4:be07a000 r3:be07a000 [ 8865.065472] [<806edd28>] (_cond_resched) from [<8002e860>] (run_ksoftirqd+0x44/0x64) [ 8865.073252] [<8002e81c>] (run_ksoftirqd) from [<800494b0>] (smpboot_thread_fn+0x124/0x190) [ 8865.081550] [<8004938c>] (smpboot_thread_fn) from [<80045928>] (kthread+0xdc/0xf8) [ 8865.089133] r10:00000000 r9:00000000 r8:00000000 r7:8004938c r6:be035740 r5:be035780 [ 8865.097097] r4:00000000 r3:be069380 [ 8865.100752] [<8004584c>] (kthread) from [<8000ed10>] (ret_from_fork+0x14/0x24) [ 8865.107990] r7:00000000 r6:00000000 r5:8004584c r4:be035780 [ 8865.113767] Code: e320f000 e4913004 e4914004 e4915004 (e4916004) [ 8865.120006] ---[ end trace b0a4c6bd499288ca ]--- [ 8865.124697] Kernel panic - not syncing: Fatal exception in interrupt [ 8865.131084] ---[ end Kernel panic - not syncing: Fatal exception in interrupt Tested-by: Peter Chen Signed-off-by: Peter Chen Signed-off-by: Fugang Duan Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/freescale/fec_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index fd655df63accae..fba3c98acd7120 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -1565,7 +1565,7 @@ fec_enet_interrupt(int irq, void *dev_id) writel(int_events, fep->hwp + FEC_IEVENT); fec_enet_collect_events(fep, int_events); - if (fep->work_tx || fep->work_rx) { + if ((fep->work_tx || fep->work_rx) && fep->link) { ret = IRQ_HANDLED; if (napi_schedule_prep(&fep->napi)) { From c683a4101fcdb247d9280b4568a38c2bacfcd03f Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Fri, 20 Feb 2015 16:53:38 -0800 Subject: [PATCH 433/788] regulator: rk808: Set the enable time for LDOs commit 28249b0c2fa361cdac450a6f40242ed45408a24f upstream. The LDOs are documented in the rk808 datasheet to have a soft start time of 400us. Add that to the driver. If this time takes longer on a certain board the device tree should be able to override with "regulator-enable-ramp-delay". This fixes some dw_mmc probing problems (together with other patches posted to the mmc maiing lists) on rk3288. Signed-off-by: Doug Anderson Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/regulator/rk808-regulator.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/regulator/rk808-regulator.c b/drivers/regulator/rk808-regulator.c index c94a3e0f3b91b4..3f6722863bd27e 100644 --- a/drivers/regulator/rk808-regulator.c +++ b/drivers/regulator/rk808-regulator.c @@ -235,6 +235,7 @@ static const struct regulator_desc rk808_reg[] = { .vsel_mask = RK808_LDO_VSEL_MASK, .enable_reg = RK808_LDO_EN_REG, .enable_mask = BIT(0), + .enable_time = 400, .owner = THIS_MODULE, }, { .name = "LDO_REG2", @@ -249,6 +250,7 @@ static const struct regulator_desc rk808_reg[] = { .vsel_mask = RK808_LDO_VSEL_MASK, .enable_reg = RK808_LDO_EN_REG, .enable_mask = BIT(1), + .enable_time = 400, .owner = THIS_MODULE, }, { .name = "LDO_REG3", @@ -263,6 +265,7 @@ static const struct regulator_desc rk808_reg[] = { .vsel_mask = RK808_BUCK4_VSEL_MASK, .enable_reg = RK808_LDO_EN_REG, .enable_mask = BIT(2), + .enable_time = 400, .owner = THIS_MODULE, }, { .name = "LDO_REG4", @@ -277,6 +280,7 @@ static const struct regulator_desc rk808_reg[] = { .vsel_mask = RK808_LDO_VSEL_MASK, .enable_reg = RK808_LDO_EN_REG, .enable_mask = BIT(3), + .enable_time = 400, .owner = THIS_MODULE, }, { .name = "LDO_REG5", @@ -291,6 +295,7 @@ static const struct regulator_desc rk808_reg[] = { .vsel_mask = RK808_LDO_VSEL_MASK, .enable_reg = RK808_LDO_EN_REG, .enable_mask = BIT(4), + .enable_time = 400, .owner = THIS_MODULE, }, { .name = "LDO_REG6", @@ -305,6 +310,7 @@ static const struct regulator_desc rk808_reg[] = { .vsel_mask = RK808_LDO_VSEL_MASK, .enable_reg = RK808_LDO_EN_REG, .enable_mask = BIT(5), + .enable_time = 400, .owner = THIS_MODULE, }, { .name = "LDO_REG7", @@ -319,6 +325,7 @@ static const struct regulator_desc rk808_reg[] = { .vsel_mask = RK808_LDO_VSEL_MASK, .enable_reg = RK808_LDO_EN_REG, .enable_mask = BIT(6), + .enable_time = 400, .owner = THIS_MODULE, }, { .name = "LDO_REG8", @@ -333,6 +340,7 @@ static const struct regulator_desc rk808_reg[] = { .vsel_mask = RK808_LDO_VSEL_MASK, .enable_reg = RK808_LDO_EN_REG, .enable_mask = BIT(7), + .enable_time = 400, .owner = THIS_MODULE, }, { .name = "SWITCH_REG1", From dcbd5ea5d603d2dab8c44023731b7b4814f8e87f Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Mon, 2 Mar 2015 21:40:39 +0100 Subject: [PATCH 434/788] regulator: Only enable disabled regulators on resume commit 0548bf4f5ad6fc3bd93c4940fa48078b34609682 upstream. The _regulator_do_enable() call ought to be a no-op when called on an already-enabled regulator. However, as an optimization _regulator_enable() doesn't call _regulator_do_enable() on an already enabled regulator. That means we never test the case of calling _regulator_do_enable() during normal usage and there may be hidden bugs or warnings. We have seen warnings issued by the tps65090 driver and bugs when using the GPIO enable pin. Let's match the same optimization that _regulator_enable() in regulator_suspend_finish(). That may speed up suspend/resume and also avoids exposing hidden bugs. [Use much clearer commit message from Doug Anderson] Signed-off-by: Javier Martinez Canillas Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/regulator/core.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 9c48fb32f6601b..61f3c5bdecf575 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -3856,9 +3856,11 @@ int regulator_suspend_finish(void) list_for_each_entry(rdev, ®ulator_list, list) { mutex_lock(&rdev->mutex); if (rdev->use_count > 0 || rdev->constraints->always_on) { - error = _regulator_do_enable(rdev); - if (error) - ret = error; + if (!_regulator_is_enabled(rdev)) { + error = _regulator_do_enable(rdev); + if (error) + ret = error; + } } else { if (!have_full_constraints()) goto unlock; From 612b78832aa4b70d15f9d0cb65f1ec9483b04abf Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Tue, 3 Mar 2015 15:20:47 -0800 Subject: [PATCH 435/788] regulator: core: Fix enable GPIO reference counting commit 29d62ec5f87fbeec8413e2215ddad12e7f972e4c upstream. Normally _regulator_do_enable() isn't called on an already-enabled rdev. That's because the main caller, _regulator_enable() always calls _regulator_is_enabled() and only calls _regulator_do_enable() if the rdev was not already enabled. However, there is one caller of _regulator_do_enable() that doesn't check: regulator_suspend_finish(). While we might want to make regulator_suspend_finish() behave more like _regulator_enable(), it's probably also a good idea to make _regulator_do_enable() robust if it is called on an already enabled rdev. At the moment, _regulator_do_enable() is _not_ robust for already enabled rdevs if we're using an ena_pin. Each time _regulator_do_enable() is called for an rdev using an ena_pin the reference count of the ena_pin is incremented even if the rdev was already enabled. This is not as intended because the ena_pin is for something else: for keeping track of how many active rdevs there are sharing the same ena_pin. Here's how the reference counting works here: * Each time _regulator_enable() is called we increment rdev->use_count, so _regulator_enable() calls need to be balanced with _regulator_disable() calls. * There is no explicit reference counting in _regulator_do_enable() which is normally just a warapper around rdev->desc->ops->enable() with code for supporting delays. It's not expected that the "ops->enable()" call do reference counting. * Since regulator_ena_gpio_ctrl() does have reference counting (handling the sharing of the pin amongst multiple rdevs), we shouldn't call it if the current rdev is already enabled. Note that as part of this we cleanup (remove) the initting of ena_gpio_state in regulator_register(). In _regulator_do_enable(), _regulator_do_disable() and _regulator_is_enabled() is is clear that ena_gpio_state should be the state of whether this particular rdev has requested the GPIO be enabled. regulator_register() was initting it as the actual state of the pin. Fixes: 967cfb18c0e3 ("regulator: core: manage enable GPIO list") Signed-off-by: Doug Anderson Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/regulator/core.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 61f3c5bdecf575..a5761d0953d80d 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1843,10 +1843,12 @@ static int _regulator_do_enable(struct regulator_dev *rdev) } if (rdev->ena_pin) { - ret = regulator_ena_gpio_ctrl(rdev, true); - if (ret < 0) - return ret; - rdev->ena_gpio_state = 1; + if (!rdev->ena_gpio_state) { + ret = regulator_ena_gpio_ctrl(rdev, true); + if (ret < 0) + return ret; + rdev->ena_gpio_state = 1; + } } else if (rdev->desc->ops->enable) { ret = rdev->desc->ops->enable(rdev); if (ret < 0) @@ -1943,10 +1945,12 @@ static int _regulator_do_disable(struct regulator_dev *rdev) trace_regulator_disable(rdev_get_name(rdev)); if (rdev->ena_pin) { - ret = regulator_ena_gpio_ctrl(rdev, false); - if (ret < 0) - return ret; - rdev->ena_gpio_state = 0; + if (rdev->ena_gpio_state) { + ret = regulator_ena_gpio_ctrl(rdev, false); + if (ret < 0) + return ret; + rdev->ena_gpio_state = 0; + } } else if (rdev->desc->ops->disable) { ret = rdev->desc->ops->disable(rdev); @@ -3678,12 +3682,6 @@ regulator_register(const struct regulator_desc *regulator_desc, config->ena_gpio, ret); goto wash; } - - if (config->ena_gpio_flags & GPIOF_OUT_INIT_HIGH) - rdev->ena_gpio_state = 1; - - if (config->ena_gpio_invert) - rdev->ena_gpio_state = !rdev->ena_gpio_state; } /* set regulator constraints */ From 01d9f895cc265aae38acf7762ef33c834a6678a5 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Thu, 12 Mar 2015 16:26:00 -0700 Subject: [PATCH 436/788] nilfs2: fix deadlock of segment constructor during recovery commit 283ee1482f349d6c0c09dfb725db5880afc56813 upstream. According to a report from Yuxuan Shui, nilfs2 in kernel 3.19 got stuck during recovery at mount time. The code path that caused the deadlock was as follows: nilfs_fill_super() load_nilfs() nilfs_salvage_orphan_logs() * Do roll-forwarding, attach segment constructor for recovery, and kick it. nilfs_segctor_thread() nilfs_segctor_thread_construct() * A lock is held with nilfs_transaction_lock() nilfs_segctor_do_construct() nilfs_segctor_drop_written_files() iput() iput_final() write_inode_now() writeback_single_inode() __writeback_single_inode() do_writepages() nilfs_writepage() nilfs_construct_dsync_segment() nilfs_transaction_lock() --> deadlock This can happen if commit 7ef3ff2fea8b ("nilfs2: fix deadlock of segment constructor over I_SYNC flag") is applied and roll-forward recovery was performed at mount time. The roll-forward recovery can happen if datasync write is done and the file system crashes immediately after that. For instance, we can reproduce the issue with the following steps: < nilfs2 is mounted on /nilfs (device: /dev/sdb1) > # dd if=/dev/zero of=/nilfs/test bs=4k count=1 && sync # dd if=/dev/zero of=/nilfs/test conv=notrunc oflag=dsync bs=4k count=1 && reboot -nfh < the system will immediately reboot > # mount -t nilfs2 /dev/sdb1 /nilfs The deadlock occurs because iput() can run segment constructor through writeback_single_inode() if MS_ACTIVE flag is not set on sb->s_flags. The above commit changed segment constructor so that it calls iput() asynchronously for inodes with i_nlink == 0, but that change was imperfect. This fixes the another deadlock by deferring iput() in segment constructor even for the case that mount is not finished, that is, for the case that MS_ACTIVE flag is not set. Signed-off-by: Ryusuke Konishi Reported-by: Yuxuan Shui Tested-by: Ryusuke Konishi Cc: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/nilfs2/segment.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c index 469086b9f99bc8..0c3f303baf32f1 100644 --- a/fs/nilfs2/segment.c +++ b/fs/nilfs2/segment.c @@ -1907,6 +1907,7 @@ static void nilfs_segctor_drop_written_files(struct nilfs_sc_info *sci, struct the_nilfs *nilfs) { struct nilfs_inode_info *ii, *n; + int during_mount = !(sci->sc_super->s_flags & MS_ACTIVE); int defer_iput = false; spin_lock(&nilfs->ns_inode_lock); @@ -1919,10 +1920,10 @@ static void nilfs_segctor_drop_written_files(struct nilfs_sc_info *sci, brelse(ii->i_bh); ii->i_bh = NULL; list_del_init(&ii->i_dirty); - if (!ii->vfs_inode.i_nlink) { + if (!ii->vfs_inode.i_nlink || during_mount) { /* - * Defer calling iput() to avoid a deadlock - * over I_SYNC flag for inodes with i_nlink == 0 + * Defer calling iput() to avoid deadlocks if + * i_nlink == 0 or mount is not yet finished. */ list_add_tail(&ii->i_dirty, &sci->sc_iput_queue); defer_iput = true; From 4b71a261c563ad9dc758ebe731b48a5d8317f73c Mon Sep 17 00:00:00 2001 From: Danesh Petigara Date: Thu, 12 Mar 2015 16:25:57 -0700 Subject: [PATCH 437/788] mm: cma: fix CMA aligned offset calculation commit 850fc430f47aad52092deaaeb32b99f97f0e6aca upstream. The CMA aligned offset calculation is incorrect for non-zero order_per_bit values. For example, if cma->order_per_bit=1, cma->base_pfn= 0x2f800000 and align_order=12, the function returns a value of 0x17c00 instead of 0x400. This patch fixes the CMA aligned offset calculation. The previous calculation was wrong and would return too-large values for the offset, so that when cma_alloc looks for free pages in the bitmap with the requested alignment > order_per_bit, it starts too far into the bitmap and so CMA allocations will fail despite there actually being plenty of free pages remaining. It will also probably have the wrong alignment. With this change, we will get the correct offset into the bitmap. One affected user is powerpc KVM, which has kvm_cma->order_per_bit set to KVM_CMA_CHUNK_ORDER - PAGE_SHIFT, or 18 - 12 = 6. [gregory.0xf0@gmail.com: changelog additions] Signed-off-by: Danesh Petigara Reviewed-by: Gregory Fong Acked-by: Michal Nazarewicz Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/cma.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/mm/cma.c b/mm/cma.c index a85ae28709a330..f1bbcb6b736882 100644 --- a/mm/cma.c +++ b/mm/cma.c @@ -64,15 +64,17 @@ static unsigned long cma_bitmap_aligned_mask(struct cma *cma, int align_order) return (1UL << (align_order - cma->order_per_bit)) - 1; } +/* + * Find a PFN aligned to the specified order and return an offset represented in + * order_per_bits. + */ static unsigned long cma_bitmap_aligned_offset(struct cma *cma, int align_order) { - unsigned int alignment; - if (align_order <= cma->order_per_bit) return 0; - alignment = 1UL << (align_order - cma->order_per_bit); - return ALIGN(cma->base_pfn, alignment) - - (cma->base_pfn >> cma->order_per_bit); + + return (ALIGN(cma->base_pfn, (1UL << align_order)) + - cma->base_pfn) >> cma->order_per_bit; } static unsigned long cma_bitmap_maxno(struct cma *cma) From 15f3667aa26cb5884cb6a1b9d1ba069be440ee66 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 11 Mar 2015 18:35:36 +0100 Subject: [PATCH 438/788] Revert "i2c: core: Dispose OF IRQ mapping at client removal time" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit a49445727014216703a3c28ccee4cef36d41571e upstream. This reverts commit e4df3a0b6228 ("i2c: core: Dispose OF IRQ mapping at client removal time") Calling irq_dispose_mapping() will destroy the mapping and disassociate the IRQ from the IRQ chip to which it belongs. Keeping it is OK, because existent mappings are reused properly. Also, this commit breaks drivers using devm* for IRQ management on OF-based systems because devm* cleanup happens in device code, after bus's remove() method returns. Signed-off-by: Jakub Kicinski Reported-by: Sébastien Szymanski Acked-by: Laurent Pinchart Acked-by: Dmitry Torokhov [wsa: updated the commit message with findings fromt the other bug report] Signed-off-by: Wolfram Sang Fixes: e4df3a0b6228 Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/i2c-core.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index e9eae57a2b50f7..63663332391da8 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -679,9 +679,6 @@ static int i2c_device_remove(struct device *dev) status = driver->remove(client); } - if (dev->of_node) - irq_dispose_mapping(client->irq); - dev_pm_domain_detach(&client->dev, true); return status; } From cd32cd8c9cab3da5dbf6d122f40e19a693a00b39 Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Thu, 5 Mar 2015 02:33:24 -0800 Subject: [PATCH 439/788] drm/vmwgfx: Reorder device takedown somewhat commit 3458390b9f0ba784481d23134798faee27b5f16f upstream. To take down the MOB and GMR memory types, the driver may have to issue fence objects and thus make sure that the fence manager is taken down after those memory types. Reorder device init accordingly. Signed-off-by: Thomas Hellstrom Reviewed-by: Sinclair Yeh Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 77 +++++++++++++++-------------- 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 6c6b655defcf4e..74a2e2318693c3 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -725,32 +725,6 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) goto out_err1; } - ret = ttm_bo_init_mm(&dev_priv->bdev, TTM_PL_VRAM, - (dev_priv->vram_size >> PAGE_SHIFT)); - if (unlikely(ret != 0)) { - DRM_ERROR("Failed initializing memory manager for VRAM.\n"); - goto out_err2; - } - - dev_priv->has_gmr = true; - if (((dev_priv->capabilities & (SVGA_CAP_GMR | SVGA_CAP_GMR2)) == 0) || - refuse_dma || ttm_bo_init_mm(&dev_priv->bdev, VMW_PL_GMR, - VMW_PL_GMR) != 0) { - DRM_INFO("No GMR memory available. " - "Graphics memory resources are very limited.\n"); - dev_priv->has_gmr = false; - } - - if (dev_priv->capabilities & SVGA_CAP_GBOBJECTS) { - dev_priv->has_mob = true; - if (ttm_bo_init_mm(&dev_priv->bdev, VMW_PL_MOB, - VMW_PL_MOB) != 0) { - DRM_INFO("No MOB memory available. " - "3D will be disabled.\n"); - dev_priv->has_mob = false; - } - } - dev_priv->mmio_mtrr = arch_phys_wc_add(dev_priv->mmio_start, dev_priv->mmio_size); @@ -813,6 +787,33 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) goto out_no_fman; } + + ret = ttm_bo_init_mm(&dev_priv->bdev, TTM_PL_VRAM, + (dev_priv->vram_size >> PAGE_SHIFT)); + if (unlikely(ret != 0)) { + DRM_ERROR("Failed initializing memory manager for VRAM.\n"); + goto out_no_vram; + } + + dev_priv->has_gmr = true; + if (((dev_priv->capabilities & (SVGA_CAP_GMR | SVGA_CAP_GMR2)) == 0) || + refuse_dma || ttm_bo_init_mm(&dev_priv->bdev, VMW_PL_GMR, + VMW_PL_GMR) != 0) { + DRM_INFO("No GMR memory available. " + "Graphics memory resources are very limited.\n"); + dev_priv->has_gmr = false; + } + + if (dev_priv->capabilities & SVGA_CAP_GBOBJECTS) { + dev_priv->has_mob = true; + if (ttm_bo_init_mm(&dev_priv->bdev, VMW_PL_MOB, + VMW_PL_MOB) != 0) { + DRM_INFO("No MOB memory available. " + "3D will be disabled.\n"); + dev_priv->has_mob = false; + } + } + vmw_kms_save_vga(dev_priv); /* Start kms and overlay systems, needs fifo. */ @@ -838,6 +839,12 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) vmw_kms_close(dev_priv); out_no_kms: vmw_kms_restore_vga(dev_priv); + if (dev_priv->has_mob) + (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_MOB); + if (dev_priv->has_gmr) + (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_GMR); + (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM); +out_no_vram: vmw_fence_manager_takedown(dev_priv->fman); out_no_fman: if (dev_priv->capabilities & SVGA_CAP_IRQMASK) @@ -853,12 +860,6 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) iounmap(dev_priv->mmio_virt); out_err3: arch_phys_wc_del(dev_priv->mmio_mtrr); - if (dev_priv->has_mob) - (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_MOB); - if (dev_priv->has_gmr) - (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_GMR); - (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM); -out_err2: (void)ttm_bo_device_release(&dev_priv->bdev); out_err1: vmw_ttm_global_release(dev_priv); @@ -887,6 +888,13 @@ static int vmw_driver_unload(struct drm_device *dev) } vmw_kms_close(dev_priv); vmw_overlay_close(dev_priv); + + if (dev_priv->has_mob) + (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_MOB); + if (dev_priv->has_gmr) + (void)ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_GMR); + (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM); + vmw_fence_manager_takedown(dev_priv->fman); if (dev_priv->capabilities & SVGA_CAP_IRQMASK) drm_irq_uninstall(dev_priv->dev); @@ -898,11 +906,6 @@ static int vmw_driver_unload(struct drm_device *dev) ttm_object_device_release(&dev_priv->tdev); iounmap(dev_priv->mmio_virt); arch_phys_wc_del(dev_priv->mmio_mtrr); - if (dev_priv->has_mob) - (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_MOB); - if (dev_priv->has_gmr) - (void)ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_GMR); - (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM); (void)ttm_bo_device_release(&dev_priv->bdev); vmw_ttm_global_release(dev_priv); From b197b203b126331d9185df0af85e578f3fc7e0d3 Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Mon, 9 Mar 2015 01:56:21 -0700 Subject: [PATCH 440/788] drm/vmwgfx: Fix a couple of lock dependency violations commit 5151adb37a5918957f4c33a8d8e7629c0fb00563 upstream. Experimental lockdep annotation added to the TTM lock has unveiled a couple of lock dependency violations in the vmwgfx driver. In both cases it turns out that the device_private::reservation_sem is not needed so the offending code is moved out of that lock. Acked-by: Sinclair Yeh Signed-off-by: Thomas Hellstrom Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 8 +++----- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 14 +++----------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index 33176d05db3542..1e114893a0016c 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -2780,13 +2780,11 @@ int vmw_execbuf_ioctl(struct drm_device *dev, void *data, NULL, arg->command_size, arg->throttle_us, (void __user *)(unsigned long)arg->fence_rep, NULL); - + ttm_read_unlock(&dev_priv->reservation_sem); if (unlikely(ret != 0)) - goto out_unlock; + return ret; vmw_kms_cursor_post_execbuf(dev_priv); -out_unlock: - ttm_read_unlock(&dev_priv->reservation_sem); - return ret; + return 0; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 8725b79e7847d6..07cda8cbbddbcb 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -2033,23 +2033,17 @@ int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, int i; struct drm_mode_config *mode_config = &dev->mode_config; - ret = ttm_read_lock(&dev_priv->reservation_sem, true); - if (unlikely(ret != 0)) - return ret; - if (!arg->num_outputs) { struct drm_vmw_rect def_rect = {0, 0, 800, 600}; vmw_du_update_layout(dev_priv, 1, &def_rect); - goto out_unlock; + return 0; } rects_size = arg->num_outputs * sizeof(struct drm_vmw_rect); rects = kcalloc(arg->num_outputs, sizeof(struct drm_vmw_rect), GFP_KERNEL); - if (unlikely(!rects)) { - ret = -ENOMEM; - goto out_unlock; - } + if (unlikely(!rects)) + return -ENOMEM; user_rects = (void __user *)(unsigned long)arg->rects; ret = copy_from_user(rects, user_rects, rects_size); @@ -2074,7 +2068,5 @@ int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, out_free: kfree(rects); -out_unlock: - ttm_read_unlock(&dev_priv->reservation_sem); return ret; } From 77f7ef95e2cf09150e5777454fd5df69af39edcd Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 25 Feb 2015 13:45:26 +0000 Subject: [PATCH 441/788] drm: Don't assign fbs for universal cursor support to files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 9a6f5130143c17b91e0a3cbf5cc2d8c1e5a80a63 upstream. The internal framebuffers we create to remap legacy cursor ioctls to plane operations for the universal plane support shouldn't be linke to the file like normal userspace framebuffers. This bug goes back to the original universal cursor plane support introduced in commit 161d0dc1dccb17ff7a38f462c7c0d4ef8bcc5662 Author: Matt Roper Date: Tue Jun 10 08:28:10 2014 -0700 drm: Support legacy cursor ioctls via universal planes when possible (v4) The isn't too disastrous since fbs are small, we only create one when the cursor bo gets changed and ultimately they'll be reaped when the window server restarts. Conceptually we'd want to just pass NULL for file_priv when creating it, but the driver needs the file to lookup the underlying buffer object for cursor id. Instead let's move the file_priv linking out of add_framebuffer_internal() into the addfb ioctl implementation, which is the only place it is needed. And also rename the function for a more accurate since it only creates the fb, but doesn't add it anywhere. Signed-off-by: Daniel Vetter (fix & commit msg) Signed-off-by: Chris Wilson (provider of lipstick) Reviewed-by: Matt Roper Cc: Ville Syrjälä Cc: Matt Roper Cc: Rob Clark Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/drm_crtc.c | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 5213da499d39fe..29168fae3dcbc2 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -42,9 +42,10 @@ #include "drm_crtc_internal.h" #include "drm_internal.h" -static struct drm_framebuffer *add_framebuffer_internal(struct drm_device *dev, - struct drm_mode_fb_cmd2 *r, - struct drm_file *file_priv); +static struct drm_framebuffer * +internal_framebuffer_create(struct drm_device *dev, + struct drm_mode_fb_cmd2 *r, + struct drm_file *file_priv); /* Avoid boilerplate. I'm tired of typing. */ #define DRM_ENUM_NAME_FN(fnname, list) \ @@ -2817,13 +2818,11 @@ static int drm_mode_cursor_universal(struct drm_crtc *crtc, */ if (req->flags & DRM_MODE_CURSOR_BO) { if (req->handle) { - fb = add_framebuffer_internal(dev, &fbreq, file_priv); + fb = internal_framebuffer_create(dev, &fbreq, file_priv); if (IS_ERR(fb)) { DRM_DEBUG_KMS("failed to wrap cursor buffer in drm framebuffer\n"); return PTR_ERR(fb); } - - drm_framebuffer_reference(fb); } else { fb = NULL; } @@ -3175,9 +3174,10 @@ static int framebuffer_check(const struct drm_mode_fb_cmd2 *r) return 0; } -static struct drm_framebuffer *add_framebuffer_internal(struct drm_device *dev, - struct drm_mode_fb_cmd2 *r, - struct drm_file *file_priv) +static struct drm_framebuffer * +internal_framebuffer_create(struct drm_device *dev, + struct drm_mode_fb_cmd2 *r, + struct drm_file *file_priv) { struct drm_mode_config *config = &dev->mode_config; struct drm_framebuffer *fb; @@ -3209,12 +3209,6 @@ static struct drm_framebuffer *add_framebuffer_internal(struct drm_device *dev, return fb; } - mutex_lock(&file_priv->fbs_lock); - r->fb_id = fb->base.id; - list_add(&fb->filp_head, &file_priv->fbs); - DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); - mutex_unlock(&file_priv->fbs_lock); - return fb; } @@ -3236,15 +3230,24 @@ static struct drm_framebuffer *add_framebuffer_internal(struct drm_device *dev, int drm_mode_addfb2(struct drm_device *dev, void *data, struct drm_file *file_priv) { + struct drm_mode_fb_cmd2 *r = data; struct drm_framebuffer *fb; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - fb = add_framebuffer_internal(dev, data, file_priv); + fb = internal_framebuffer_create(dev, r, file_priv); if (IS_ERR(fb)) return PTR_ERR(fb); + /* Transfer ownership to the filp for reaping on close */ + + DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); + mutex_lock(&file_priv->fbs_lock); + r->fb_id = fb->base.id; + list_add(&fb->filp_head, &file_priv->fbs); + mutex_unlock(&file_priv->fbs_lock); + return 0; } From 61e975e46864cbcf7707c980173bc7efe781edb5 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 8 Jan 2015 17:54:13 +0200 Subject: [PATCH 442/788] drm/i915: add dev_to_i915 helper commit 888d0d421663313739a8bf93459c6ba61fd4b121 upstream. This will be needed by later patches, so factor it out. No functional change. v2: - s/dev_to_i915_priv/dev_to_i915/ (Jani) - don't use the helper in i915_pm_suspend (Chris) - simplify the helper (Chris) v3: - remove redundant upcasting in the helper (Daniel) Signed-off-by: Imre Deak Reviewed-by: Takashi Iwai Signed-off-by: Daniel Vetter Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/i915_drv.c | 9 +++------ drivers/gpu/drm/i915/i915_drv.h | 5 +++++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 7643300828c3ae..489b220af02b60 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -934,8 +934,7 @@ static int i915_pm_suspend(struct device *dev) static int i915_pm_suspend_late(struct device *dev) { - struct pci_dev *pdev = to_pci_dev(dev); - struct drm_device *drm_dev = pci_get_drvdata(pdev); + struct drm_device *drm_dev = dev_to_i915(dev)->dev; /* * We have a suspedn ordering issue with the snd-hda driver also @@ -954,8 +953,7 @@ static int i915_pm_suspend_late(struct device *dev) static int i915_pm_resume_early(struct device *dev) { - struct pci_dev *pdev = to_pci_dev(dev); - struct drm_device *drm_dev = pci_get_drvdata(pdev); + struct drm_device *drm_dev = dev_to_i915(dev)->dev; if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; @@ -965,8 +963,7 @@ static int i915_pm_resume_early(struct device *dev) static int i915_pm_resume(struct device *dev) { - struct pci_dev *pdev = to_pci_dev(dev); - struct drm_device *drm_dev = pci_get_drvdata(pdev); + struct drm_device *drm_dev = dev_to_i915(dev)->dev; if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 0936b0f94826b9..ddd005ce3a9499 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1781,6 +1781,11 @@ static inline struct drm_i915_private *to_i915(const struct drm_device *dev) return dev->dev_private; } +static inline struct drm_i915_private *dev_to_i915(struct device *dev) +{ + return to_i915(dev_get_drvdata(dev)); +} + /* Iterate over initialised rings */ #define for_each_ring(ring__, dev_priv__, i__) \ for ((i__) = 0; (i__) < I915_NUM_RINGS; (i__)++) \ From 64e8548fcb078d676bf08d6ddd60ebb7014f7bbf Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 2 Mar 2015 13:04:41 +0200 Subject: [PATCH 443/788] drm/i915: gen4: work around hang during hibernation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit ab3be73fa7b43f4c3648ce29b5fd649ea54d3adb upstream. Bjørn reported that his machine hang during hibernation and eventually bisected the problem to the following commit: commit da2bc1b9db3351addd293e5b82757efe1f77ed1d Author: Imre Deak Date: Thu Oct 23 19:23:26 2014 +0300 drm/i915: add poweroff_late handler The problem seems to be that after the kernel puts the device into D3 the BIOS still tries to access it, or otherwise assumes that it's in D0. This is clearly bogus, since ACPI mandates that devices are put into D3 by the OSPM if they are not wake-up sources. In the future we want to unify more of the driver's runtime and system suspend paths, for example by skipping all the system suspend/hibernation hooks if the device is runtime suspended already. Accordingly for all other platforms the goal is still to properly power down the device during hibernation. v2: - Another GEN4 Lenovo laptop had the same issue, while platforms from other vendors (including mobile and desktop, GEN4 and non-GEN4) seem to work fine. Based on this apply the workaround on all GEN4 Lenovo platforms. - add code comment about failing platforms (Ville) Reference: http://lists.freedesktop.org/archives/intel-gfx/2015-February/060633.html Reported-and-bisected-by: Bjørn Mork Signed-off-by: Imre Deak Acked-by: Daniel Vetter Signed-off-by: Jani Nikula Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/i915_drv.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 489b220af02b60..4e6405e7226fb8 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -622,7 +622,7 @@ static int i915_drm_suspend(struct drm_device *dev) return 0; } -static int i915_drm_suspend_late(struct drm_device *drm_dev) +static int i915_drm_suspend_late(struct drm_device *drm_dev, bool hibernation) { struct drm_i915_private *dev_priv = drm_dev->dev_private; int ret; @@ -636,7 +636,17 @@ static int i915_drm_suspend_late(struct drm_device *drm_dev) } pci_disable_device(drm_dev->pdev); - pci_set_power_state(drm_dev->pdev, PCI_D3hot); + /* + * During hibernation on some GEN4 platforms the BIOS may try to access + * the device even though it's already in D3 and hang the machine. So + * leave the device in D0 on those platforms and hope the BIOS will + * power down the device properly. Platforms where this was seen: + * Lenovo Thinkpad X301, X61s + */ + if (!(hibernation && + drm_dev->pdev->subsystem_vendor == PCI_VENDOR_ID_LENOVO && + INTEL_INFO(dev_priv)->gen == 4)) + pci_set_power_state(drm_dev->pdev, PCI_D3hot); return 0; } @@ -662,7 +672,7 @@ int i915_suspend_legacy(struct drm_device *dev, pm_message_t state) if (error) return error; - return i915_drm_suspend_late(dev); + return i915_drm_suspend_late(dev, false); } static int i915_drm_resume(struct drm_device *dev) @@ -948,7 +958,17 @@ static int i915_pm_suspend_late(struct device *dev) if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; - return i915_drm_suspend_late(drm_dev); + return i915_drm_suspend_late(drm_dev, false); +} + +static int i915_pm_poweroff_late(struct device *dev) +{ + struct drm_device *drm_dev = dev_to_i915(dev)->dev; + + if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF) + return 0; + + return i915_drm_suspend_late(drm_dev, true); } static int i915_pm_resume_early(struct device *dev) @@ -1514,7 +1534,7 @@ static const struct dev_pm_ops i915_pm_ops = { .thaw_early = i915_pm_resume_early, .thaw = i915_pm_resume, .poweroff = i915_pm_suspend, - .poweroff_late = i915_pm_suspend_late, + .poweroff_late = i915_pm_poweroff_late, .restore_early = i915_pm_resume_early, .restore = i915_pm_resume, From 7943518004891dbe06eeb6521f2cfb41c64911b4 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 12 Mar 2015 16:25:49 -0700 Subject: [PATCH 444/788] drivers/rtc/rtc-s3c.c: add .needs_src_clk to s3c6410 RTC data commit 8792f7772f4f40ffc68bad5f28311205584b734d upstream. Commit df9e26d093d3 ("rtc: s3c: add support for RTC of Exynos3250 SoC") added an "rtc_src" DT property to specify the clock used as a source to the S3C real-time clock. Not all SoCs needs this so commit eaf3a659086e ("drivers/rtc/rtc-s3c.c: fix initialization failure without rtc source clock") changed to check the struct s3c_rtc_data .needs_src_clk to conditionally grab the clock. But that commit didn't update the data for each IP version so the RTC broke on the boards that needs a source clock. This is the case of at least Exynos5250 and Exynos5440 which uses the s3c6410 RTC IP block. This commit fixes the S3C rtc on the Exynos5250 Snow and Exynos5420 Peach Pit and Pi Chromebooks. Signed-off-by: Javier Martinez Canillas Cc: Marek Szyprowski Cc: Chanwoo Choi Cc: Doug Anderson Cc: Olof Johansson Cc: Kevin Hilman Cc: Tyler Baker Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- drivers/rtc/rtc-s3c.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index 4241eeab3386ab..f4cf6851fae971 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -849,6 +849,7 @@ static struct s3c_rtc_data const s3c2443_rtc_data = { static struct s3c_rtc_data const s3c6410_rtc_data = { .max_user_freq = 32768, + .needs_src_clk = true, .irq_handler = s3c6410_rtc_irq, .set_freq = s3c6410_rtc_setfreq, .enable_tick = s3c6410_rtc_enable_tick, From 952b4fea79c26dcec36676d83c8b908c7b150e68 Mon Sep 17 00:00:00 2001 From: Juergen Gross Date: Thu, 26 Feb 2015 06:52:05 +0100 Subject: [PATCH 445/788] xen/events: avoid NULL pointer dereference in dom0 on large machines commit 85e40b0539b24518c8bdf63e2605c8522377d00f upstream. Using the pvops kernel a NULL pointer dereference was detected on a large machine (144 processors) when booting as dom0 in evtchn_fifo_unmask() during assignment of a pirq. The event channel in question was the first to need a new entry in event_array[] in events_fifo.c. Unfortunately xen_irq_info_pirq_setup() is called with evtchn being 0 for a new pirq and the real event channel number is assigned to the pirq only during __startup_pirq(). It is mandatory to call xen_evtchn_port_setup() after assigning the event channel number to the pirq to make sure all memory needed for the event channel is allocated. Signed-off-by: Juergen Gross Signed-off-by: David Vrabel Signed-off-by: Greg Kroah-Hartman --- drivers/xen/events/events_base.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/xen/events/events_base.c b/drivers/xen/events/events_base.c index b4bca2d4a7e53c..70fba973a10716 100644 --- a/drivers/xen/events/events_base.c +++ b/drivers/xen/events/events_base.c @@ -526,20 +526,26 @@ static unsigned int __startup_pirq(unsigned int irq) pirq_query_unmask(irq); rc = set_evtchn_to_irq(evtchn, irq); - if (rc != 0) { - pr_err("irq%d: Failed to set port to irq mapping (%d)\n", - irq, rc); - xen_evtchn_close(evtchn); - return 0; - } + if (rc) + goto err; + bind_evtchn_to_cpu(evtchn, 0); info->evtchn = evtchn; + rc = xen_evtchn_port_setup(info); + if (rc) + goto err; + out: unmask_evtchn(evtchn); eoi_pirq(irq_get_irq_data(irq)); return 0; + +err: + pr_err("irq%d: Failed to set port to irq mapping (%d)\n", irq, rc); + xen_evtchn_close(evtchn); + return 0; } static unsigned int startup_pirq(struct irq_data *data) From 3d60a2bc53f2ba4c4bcf889d0c10841c4c702bac Mon Sep 17 00:00:00 2001 From: Juergen Gross Date: Fri, 27 Feb 2015 15:45:29 +0100 Subject: [PATCH 446/788] x86/xen: correct bug in p2m list initialization commit b8f05c8803fce899d79ca66f8d7f348cf15fb40e upstream. Commit 054954eb051f35e74b75a566a96fe756015352c8 ("xen: switch to linear virtual mapped sparse p2m list") introduced an error. During initialization of the p2m list a p2m identity area mapped by a complete identity pmd entry has to be split up into smaller chunks sometimes, if a non-identity pfn is introduced in this area. If this non-identity pfn is not at index 0 of a p2m page the new p2m page needed is initialized with wrong identity entries, as the identity pfns don't start with the value corresponding to index 0, but with the initial non-identity pfn. This results in weird wrong mappings. Correct the wrong initialization by starting with the correct pfn. Reported-by: Stefan Bader Signed-off-by: Juergen Gross Tested-by: Stefan Bader Signed-off-by: David Vrabel Signed-off-by: Greg Kroah-Hartman --- arch/x86/xen/p2m.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/xen/p2m.c b/arch/x86/xen/p2m.c index 376a0a9dc670b4..cc45a9f1a6e60b 100644 --- a/arch/x86/xen/p2m.c +++ b/arch/x86/xen/p2m.c @@ -567,7 +567,7 @@ static bool alloc_p2m(unsigned long pfn) if (p2m_pfn == PFN_DOWN(__pa(p2m_missing))) p2m_init(p2m); else - p2m_init_identity(p2m, pfn); + p2m_init_identity(p2m, pfn & ~(P2M_PER_PAGE - 1)); spin_lock_irqsave(&p2m_update_lock, flags); From 0cb2cf302bcecea9ec645fdd1c40e81f9a274cde Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Wed, 11 Mar 2015 13:51:17 +0000 Subject: [PATCH 447/788] xen-pciback: limit guest control of command register commit af6fc858a35b90e89ea7a7ee58e66628c55c776b upstream. Otherwise the guest can abuse that control to cause e.g. PCIe Unsupported Request responses by disabling memory and/or I/O decoding and subsequently causing (CPU side) accesses to the respective address ranges, which (depending on system configuration) may be fatal to the host. Note that to alter any of the bits collected together as PCI_COMMAND_GUEST permissive mode is now required to be enabled globally or on the specific device. This is CVE-2015-2150 / XSA-120. Signed-off-by: Jan Beulich Reviewed-by: Konrad Rzeszutek Wilk Signed-off-by: David Vrabel Signed-off-by: Greg Kroah-Hartman --- drivers/xen/xen-pciback/conf_space.c | 2 +- drivers/xen/xen-pciback/conf_space.h | 2 + drivers/xen/xen-pciback/conf_space_header.c | 61 ++++++++++++++++----- 3 files changed, 51 insertions(+), 14 deletions(-) diff --git a/drivers/xen/xen-pciback/conf_space.c b/drivers/xen/xen-pciback/conf_space.c index 46ae0f9f02adcc..75fe3d466515a0 100644 --- a/drivers/xen/xen-pciback/conf_space.c +++ b/drivers/xen/xen-pciback/conf_space.c @@ -16,7 +16,7 @@ #include "conf_space.h" #include "conf_space_quirks.h" -static bool permissive; +bool permissive; module_param(permissive, bool, 0644); /* This is where xen_pcibk_read_config_byte, xen_pcibk_read_config_word, diff --git a/drivers/xen/xen-pciback/conf_space.h b/drivers/xen/xen-pciback/conf_space.h index e56c934ad137be..2e1d73d1d5d093 100644 --- a/drivers/xen/xen-pciback/conf_space.h +++ b/drivers/xen/xen-pciback/conf_space.h @@ -64,6 +64,8 @@ struct config_field_entry { void *data; }; +extern bool permissive; + #define OFFSET(cfg_entry) ((cfg_entry)->base_offset+(cfg_entry)->field->offset) /* Add fields to a device - the add_fields macro expects to get a pointer to diff --git a/drivers/xen/xen-pciback/conf_space_header.c b/drivers/xen/xen-pciback/conf_space_header.c index c5ee82587e8cc3..2d7369391472fd 100644 --- a/drivers/xen/xen-pciback/conf_space_header.c +++ b/drivers/xen/xen-pciback/conf_space_header.c @@ -11,6 +11,10 @@ #include "pciback.h" #include "conf_space.h" +struct pci_cmd_info { + u16 val; +}; + struct pci_bar_info { u32 val; u32 len_val; @@ -20,22 +24,36 @@ struct pci_bar_info { #define is_enable_cmd(value) ((value)&(PCI_COMMAND_MEMORY|PCI_COMMAND_IO)) #define is_master_cmd(value) ((value)&PCI_COMMAND_MASTER) -static int command_read(struct pci_dev *dev, int offset, u16 *value, void *data) +/* Bits guests are allowed to control in permissive mode. */ +#define PCI_COMMAND_GUEST (PCI_COMMAND_MASTER|PCI_COMMAND_SPECIAL| \ + PCI_COMMAND_INVALIDATE|PCI_COMMAND_VGA_PALETTE| \ + PCI_COMMAND_WAIT|PCI_COMMAND_FAST_BACK) + +static void *command_init(struct pci_dev *dev, int offset) { - int i; - int ret; - - ret = xen_pcibk_read_config_word(dev, offset, value, data); - if (!pci_is_enabled(dev)) - return ret; - - for (i = 0; i < PCI_ROM_RESOURCE; i++) { - if (dev->resource[i].flags & IORESOURCE_IO) - *value |= PCI_COMMAND_IO; - if (dev->resource[i].flags & IORESOURCE_MEM) - *value |= PCI_COMMAND_MEMORY; + struct pci_cmd_info *cmd = kmalloc(sizeof(*cmd), GFP_KERNEL); + int err; + + if (!cmd) + return ERR_PTR(-ENOMEM); + + err = pci_read_config_word(dev, PCI_COMMAND, &cmd->val); + if (err) { + kfree(cmd); + return ERR_PTR(err); } + return cmd; +} + +static int command_read(struct pci_dev *dev, int offset, u16 *value, void *data) +{ + int ret = pci_read_config_word(dev, offset, value); + const struct pci_cmd_info *cmd = data; + + *value &= PCI_COMMAND_GUEST; + *value |= cmd->val & ~PCI_COMMAND_GUEST; + return ret; } @@ -43,6 +61,8 @@ static int command_write(struct pci_dev *dev, int offset, u16 value, void *data) { struct xen_pcibk_dev_data *dev_data; int err; + u16 val; + struct pci_cmd_info *cmd = data; dev_data = pci_get_drvdata(dev); if (!pci_is_enabled(dev) && is_enable_cmd(value)) { @@ -83,6 +103,19 @@ static int command_write(struct pci_dev *dev, int offset, u16 value, void *data) } } + cmd->val = value; + + if (!permissive && (!dev_data || !dev_data->permissive)) + return 0; + + /* Only allow the guest to control certain bits. */ + err = pci_read_config_word(dev, offset, &val); + if (err || val == value) + return err; + + value &= PCI_COMMAND_GUEST; + value |= val & ~PCI_COMMAND_GUEST; + return pci_write_config_word(dev, offset, value); } @@ -282,6 +315,8 @@ static const struct config_field header_common[] = { { .offset = PCI_COMMAND, .size = 2, + .init = command_init, + .release = bar_release, .u.w.read = command_read, .u.w.write = command_write, }, From 8b78939bb04654d3b02ffb66d6b9d2a223119663 Mon Sep 17 00:00:00 2001 From: Leif Lindholm Date: Fri, 6 Mar 2015 16:52:53 +0000 Subject: [PATCH 448/788] of: fix handling of '/' in options for of_find_node_by_path() commit 106937e8ccdcf0f4b95fbf0fe9abd42766cade33 upstream. Ensure proper handling of paths with appended options (after ':'), where those options may contain a '/'. Fixes: 7914a7c5651a ("of: support passing console options with stdout-path") Reported-by: Peter Hurley Signed-off-by: Leif Lindholm Signed-off-by: Rob Herring Signed-off-by: Greg Kroah-Hartman --- drivers/of/base.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/drivers/of/base.c b/drivers/of/base.c index 36536b6a8834ac..c60b2c07018533 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -714,16 +714,17 @@ static struct device_node *__of_find_node_by_path(struct device_node *parent, const char *path) { struct device_node *child; - int len = strchrnul(path, '/') - path; - int term; + int len; + const char *end; + end = strchr(path, ':'); + if (!end) + end = strchrnul(path, '/'); + + len = end - path; if (!len) return NULL; - term = strchrnul(path, ':') - path; - if (term < len) - len = term; - __for_each_child_of_node(parent, child) { const char *name = strrchr(child->full_name, '/'); if (WARN(!name, "malformed device_node %s\n", child->full_name)) @@ -768,8 +769,12 @@ struct device_node *of_find_node_opts_by_path(const char *path, const char **opt /* The path could begin with an alias */ if (*path != '/') { - char *p = strchrnul(path, '/'); - int len = separator ? separator - path : p - path; + int len; + const char *p = separator; + + if (!p) + p = strchrnul(path, '/'); + len = p - path; /* of_aliases must not be NULL */ if (!of_aliases) @@ -794,6 +799,8 @@ struct device_node *of_find_node_opts_by_path(const char *path, const char **opt path++; /* Increment past '/' delimiter */ np = __of_find_node_by_path(np, path); path = strchrnul(path, '/'); + if (separator && separator < path) + break; } raw_spin_unlock_irqrestore(&devtree_lock, flags); return np; From 5f83b8d79d822abbc73f9efb48aa59dc8c679415 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 17 Mar 2015 12:30:31 -0700 Subject: [PATCH 449/788] of: handle both '/' and ':' in path strings commit 721a09e95c786346b4188863a1cfa3909c76f690 upstream. Commit 106937e8ccdc ("of: fix handling of '/' in options for of_find_node_by_path()") caused a regression in OF handling of stdout-path. While it fixes some cases which have '/' after the ':', it breaks cases where there is more than one '/' *before* the ':'. For example, it breaks this boot string stdout-path = "/rdb/serial@f040ab00:115200"; So rather than doing sequentialized checks (first for '/', then for ':'; or vice versa), to get the correct behavior we need to check for the first occurrence of either one of them. It so happens that the handy strcspn() helper can do just that. Fixes: 106937e8ccdc ("of: fix handling of '/' in options for of_find_node_by_path()") Signed-off-by: Brian Norris Acked-by: Leif Lindholm Signed-off-by: Rob Herring Signed-off-by: Greg Kroah-Hartman --- drivers/of/base.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/of/base.c b/drivers/of/base.c index c60b2c07018533..65a47f4dcac642 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -715,13 +715,8 @@ static struct device_node *__of_find_node_by_path(struct device_node *parent, { struct device_node *child; int len; - const char *end; - end = strchr(path, ':'); - if (!end) - end = strchrnul(path, '/'); - - len = end - path; + len = strcspn(path, "/:"); if (!len) return NULL; From c0af7a7bbde5d6c542410cdd5660b4030b83fa30 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 6 Feb 2015 02:07:45 -0500 Subject: [PATCH 450/788] gadgetfs: use-after-free in ->aio_read() commit f01d35a15fa04162a58b95970fc01fa70ec9dacd upstream. AIO_PREAD requests call ->aio_read() with iovec on caller's stack, so if we are going to access it asynchronously, we'd better get ourselves a copy - the one on kernel stack of aio_run_iocb() won't be there anymore. function/f_fs.c take care of doing that, legacy/inode.c doesn't... Signed-off-by: Al Viro Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/legacy/inode.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index db49ec4c748e94..9fbbaa041a3106 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -566,7 +566,6 @@ static ssize_t ep_copy_to_user(struct kiocb_priv *priv) if (total == 0) break; } - return len; } @@ -585,6 +584,7 @@ static void ep_user_copy_worker(struct work_struct *work) aio_complete(iocb, ret, ret); kfree(priv->buf); + kfree(priv->iv); kfree(priv); } @@ -605,6 +605,7 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req) */ if (priv->iv == NULL || unlikely(req->actual == 0)) { kfree(req->buf); + kfree(priv->iv); kfree(priv); iocb->private = NULL; /* aio_complete() reports bytes-transferred _and_ faults */ @@ -640,7 +641,7 @@ ep_aio_rwtail( struct usb_request *req; ssize_t value; - priv = kmalloc(sizeof *priv, GFP_KERNEL); + priv = kzalloc(sizeof *priv, GFP_KERNEL); if (!priv) { value = -ENOMEM; fail: @@ -649,7 +650,14 @@ ep_aio_rwtail( } iocb->private = priv; priv->iocb = iocb; - priv->iv = iv; + if (iv) { + priv->iv = kmemdup(iv, nr_segs * sizeof(struct iovec), + GFP_KERNEL); + if (!priv->iv) { + kfree(priv); + goto fail; + } + } priv->nr_segs = nr_segs; INIT_WORK(&priv->work, ep_user_copy_worker); @@ -689,6 +697,7 @@ ep_aio_rwtail( mutex_unlock(&epdata->lock); if (unlikely(value)) { + kfree(priv->iv); kfree(priv); put_ep(epdata); } else From 9db7ae21871deebdebe333b469f9926e4048b1a9 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Wed, 4 Mar 2015 16:18:33 -0800 Subject: [PATCH 451/788] libsas: Fix Kernel Crash in smp_execute_task commit 6302ce4d80aa82b3fdb5c5cd68e7268037091b47 upstream. This crash was reported: [ 366.947370] sd 3:0:1:0: [sdb] Spinning up disk.... [ 368.804046] BUG: unable to handle kernel NULL pointer dereference at (null) [ 368.804072] IP: [] __mutex_lock_common.isra.7+0x9c/0x15b [ 368.804098] PGD 0 [ 368.804114] Oops: 0002 [#1] SMP [ 368.804143] CPU 1 [ 368.804151] Modules linked in: sg netconsole s3g(PO) uinput joydev hid_multitouch usbhid hid snd_hda_codec_via cpufreq_userspace cpufreq_powersave cpufreq_stats uhci_hcd cpufreq_conservative snd_hda_intel snd_hda_codec snd_hwdep snd_pcm sdhci_pci snd_page_alloc sdhci snd_timer snd psmouse evdev serio_raw pcspkr soundcore xhci_hcd shpchp s3g_drm(O) mvsas mmc_core ahci libahci drm i2c_core acpi_cpufreq mperf video processor button thermal_sys dm_dmirror exfat_fs exfat_core dm_zcache dm_mod padlock_aes aes_generic padlock_sha iscsi_target_mod target_core_mod configfs sswipe libsas libata scsi_transport_sas picdev via_cputemp hwmon_vid fuse parport_pc ppdev lp parport autofs4 ext4 crc16 mbcache jbd2 sd_mod crc_t10dif usb_storage scsi_mod ehci_hcd usbcore usb_common [ 368.804749] [ 368.804764] Pid: 392, comm: kworker/u:3 Tainted: P W O 3.4.87-logicube-ng.22 #1 To be filled by O.E.M. To be filled by O.E.M./EPIA-M920 [ 368.804802] RIP: 0010:[] [] __mutex_lock_common.isra.7+0x9c/0x15b [ 368.804827] RSP: 0018:ffff880117001cc0 EFLAGS: 00010246 [ 368.804842] RAX: 0000000000000000 RBX: ffff8801185030d0 RCX: ffff88008edcb420 [ 368.804857] RDX: 0000000000000000 RSI: 0000000000000002 RDI: ffff8801185030d4 [ 368.804873] RBP: ffff8801181531c0 R08: 0000000000000020 R09: 00000000fffffffe [ 368.804885] R10: 0000000000000000 R11: 0000000000000000 R12: ffff8801185030d4 [ 368.804899] R13: 0000000000000002 R14: ffff880117001fd8 R15: ffff8801185030d8 [ 368.804916] FS: 0000000000000000(0000) GS:ffff88011fc80000(0000) knlGS:0000000000000000 [ 368.804931] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [ 368.804946] CR2: 0000000000000000 CR3: 000000000160b000 CR4: 00000000000006e0 [ 368.804962] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 368.804978] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [ 368.804995] Process kworker/u:3 (pid: 392, threadinfo ffff880117000000, task ffff8801181531c0) [ 368.805009] Stack: [ 368.805017] ffff8801185030d8 0000000000000000 ffffffff8161ddf0 ffffffff81056f7c [ 368.805062] 000000000000b503 ffff8801185030d0 ffff880118503000 0000000000000000 [ 368.805100] ffff8801185030d0 ffff8801188b8000 ffff88008edcb420 ffffffff813583ac [ 368.805135] Call Trace: [ 368.805153] [] ? up+0xb/0x33 [ 368.805168] [] ? mutex_lock+0x16/0x25 [ 368.805194] [] ? smp_execute_task+0x4e/0x222 [libsas] [ 368.805217] [] ? sas_find_bcast_dev+0x3c/0x15d [libsas] [ 368.805240] [] ? sas_find_bcast_dev+0x6f/0x15d [libsas] [ 368.805264] [] ? sas_ex_revalidate_domain+0x37/0x2ec [libsas] [ 368.805280] [] ? printk+0x43/0x48 [ 368.805296] [] ? _raw_spin_unlock_irqrestore+0xc/0xd [ 368.805318] [] ? sas_revalidate_domain+0x85/0xb6 [libsas] [ 368.805336] [] ? process_one_work+0x151/0x27c [ 368.805351] [] ? worker_thread+0xbb/0x152 [ 368.805366] [] ? manage_workers.isra.29+0x163/0x163 [ 368.805382] [] ? kthread+0x79/0x81 [ 368.805399] [] ? kernel_thread_helper+0x4/0x10 [ 368.805416] [] ? kthread_flush_work_fn+0x9/0x9 [ 368.805431] [] ? gs_change+0x13/0x13 [ 368.805442] Code: 83 7d 30 63 7e 04 f3 90 eb ab 4c 8d 63 04 4c 8d 7b 08 4c 89 e7 e8 fa 15 00 00 48 8b 43 10 4c 89 3c 24 48 89 63 10 48 89 44 24 08 <48> 89 20 83 c8 ff 48 89 6c 24 10 87 03 ff c8 74 35 4d 89 ee 41 [ 368.805851] RIP [] __mutex_lock_common.isra.7+0x9c/0x15b [ 368.805877] RSP [ 368.805886] CR2: 0000000000000000 [ 368.805899] ---[ end trace b720682065d8f4cc ]--- It's directly caused by 89d3cf6 [SCSI] libsas: add mutex for SMP task execution, but shows a deeper cause: expander functions expect to be able to cast to and treat domain devices as expanders. The correct fix is to only do expander discover when we know we've got an expander device to avoid wrongly casting a non-expander device. Reported-by: Praveen Murali Tested-by: Praveen Murali Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/libsas/sas_discover.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 62b58d38ce2e63..60de66252fa2b9 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -500,6 +500,7 @@ static void sas_revalidate_domain(struct work_struct *work) struct sas_discovery_event *ev = to_sas_discovery_event(work); struct asd_sas_port *port = ev->port; struct sas_ha_struct *ha = port->ha; + struct domain_device *ddev = port->port_dev; /* prevent revalidation from finding sata links in recovery */ mutex_lock(&ha->disco_mutex); @@ -514,8 +515,9 @@ static void sas_revalidate_domain(struct work_struct *work) SAS_DPRINTK("REVALIDATING DOMAIN on port %d, pid:%d\n", port->id, task_pid_nr(current)); - if (port->port_dev) - res = sas_ex_revalidate_domain(port->port_dev); + if (ddev && (ddev->dev_type == SAS_FANOUT_EXPANDER_DEVICE || + ddev->dev_type == SAS_EDGE_EXPANDER_DEVICE)) + res = sas_ex_revalidate_domain(ddev); SAS_DPRINTK("done REVALIDATING DOMAIN on port %d, pid:%d, res 0x%x\n", port->id, task_pid_nr(current), res); From 58815a809a3fd8408ff45c224c4d8cb7cd7a35cb Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Wed, 4 Feb 2015 17:38:15 -0500 Subject: [PATCH 452/788] PCI: Don't read past the end of sysfs "driver_override" buffer commit 4efe874aace57dba967624ce1c48322da2447b75 upstream. When printing the driver_override parameter when it is 4095 and 4094 bytes long, the printing code would access invalid memory because we need count+1 bytes for printing. Fixes: 782a985d7af2 ("PCI: Introduce new device binding path using pci_dev.driver_override") Signed-off-by: Sasha Levin Signed-off-by: Bjorn Helgaas Acked-by: Alex Williamson CC: Konrad Rzeszutek Wilk CC: Alexander Graf CC: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/pci/pci-sysfs.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index aa012fb3834b48..312f23a8429cd9 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -521,7 +521,8 @@ static ssize_t driver_override_store(struct device *dev, struct pci_dev *pdev = to_pci_dev(dev); char *driver_override, *old = pdev->driver_override, *cp; - if (count > PATH_MAX) + /* We need to keep extra room for a newline */ + if (count >= (PAGE_SIZE - 1)) return -EINVAL; driver_override = kstrndup(buf, count, GFP_KERNEL); @@ -549,7 +550,7 @@ static ssize_t driver_override_show(struct device *dev, { struct pci_dev *pdev = to_pci_dev(dev); - return sprintf(buf, "%s\n", pdev->driver_override); + return snprintf(buf, PAGE_SIZE, "%s\n", pdev->driver_override); } static DEVICE_ATTR_RW(driver_override); From 940f85f84864efa1d2ae1ddd9aeac287f619cf2e Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 3 Mar 2015 11:27:23 +0100 Subject: [PATCH 453/788] irqchip: armada-370-xp: Fix chained per-cpu interrupts commit 5724be8464dceac047c1eaddaa3651cea0ec16ca upstream. On the Cortex-A9-based Armada SoCs, the MPIC is not the primary interrupt controller. Yet, it still has to handle some per-cpu interrupt. To do so, it is chained with the GIC using a per-cpu interrupt. However, the current code only call irq_set_chained_handler, which is called and enable that interrupt only on the boot CPU, which means that the parent per-CPU interrupt is never unmasked on the secondary CPUs, preventing the per-CPU interrupt to actually work as expected. This was not seen until now since the only MPIC PPI users were the Marvell timers that were not working, but not used either since the system use the ARM TWD by default, and the ethernet controllers, that are faking there interrupts as SPI, and don't really expect to have interrupts on the secondary cores anyway. Add a CPU notifier that will enable the PPI on the secondary cores when they are brought up. Signed-off-by: Maxime Ripard Acked-by: Gregory CLEMENT Link: https://lkml.kernel.org/r/1425378443-28822-1-git-send-email-maxime.ripard@free-electrons.com Signed-off-by: Jason Cooper Signed-off-by: Greg Kroah-Hartman --- drivers/irqchip/irq-armada-370-xp.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c index 463c235acbdcdc..4387dae14e453a 100644 --- a/drivers/irqchip/irq-armada-370-xp.c +++ b/drivers/irqchip/irq-armada-370-xp.c @@ -69,6 +69,7 @@ static void __iomem *per_cpu_int_base; static void __iomem *main_int_base; static struct irq_domain *armada_370_xp_mpic_domain; static u32 doorbell_mask_reg; +static int parent_irq; #ifdef CONFIG_PCI_MSI static struct irq_domain *armada_370_xp_msi_domain; static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR); @@ -356,6 +357,7 @@ static int armada_xp_mpic_secondary_init(struct notifier_block *nfb, { if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) armada_xp_mpic_smp_cpu_init(); + return NOTIFY_OK; } @@ -364,6 +366,20 @@ static struct notifier_block armada_370_xp_mpic_cpu_notifier = { .priority = 100, }; +static int mpic_cascaded_secondary_init(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) + enable_percpu_irq(parent_irq, IRQ_TYPE_NONE); + + return NOTIFY_OK; +} + +static struct notifier_block mpic_cascaded_cpu_notifier = { + .notifier_call = mpic_cascaded_secondary_init, + .priority = 100, +}; + #endif /* CONFIG_SMP */ static struct irq_domain_ops armada_370_xp_mpic_irq_ops = { @@ -539,7 +555,7 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, struct device_node *parent) { struct resource main_int_res, per_cpu_int_res; - int parent_irq, nr_irqs, i; + int nr_irqs, i; u32 control; BUG_ON(of_address_to_resource(node, 0, &main_int_res)); @@ -587,6 +603,9 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, register_cpu_notifier(&armada_370_xp_mpic_cpu_notifier); #endif } else { +#ifdef CONFIG_SMP + register_cpu_notifier(&mpic_cascaded_cpu_notifier); +#endif irq_set_chained_handler(parent_irq, armada_370_xp_mpic_handle_cascade_irq); } From e3b6833de75b591d8537e3f08ea9df5608ee6281 Mon Sep 17 00:00:00 2001 From: "Kirill A. Shutemov" Date: Mon, 9 Mar 2015 23:11:12 +0200 Subject: [PATCH 454/788] pagemap: do not leak physical addresses to non-privileged userspace commit ab676b7d6fbf4b294bf198fb27ade5b0e865c7ce upstream. As pointed by recent post[1] on exploiting DRAM physical imperfection, /proc/PID/pagemap exposes sensitive information which can be used to do attacks. This disallows anybody without CAP_SYS_ADMIN to read the pagemap. [1] http://googleprojectzero.blogspot.com/2015/03/exploiting-dram-rowhammer-bug-to-gain.html [ Eventually we might want to do anything more finegrained, but for now this is the simple model. - Linus ] Signed-off-by: Kirill A. Shutemov Acked-by: Konstantin Khlebnikov Acked-by: Andy Lutomirski Cc: Pavel Emelyanov Cc: Andrew Morton Cc: Mark Seaborn Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/proc/task_mmu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 88f9b8352742c7..f86e5499caec76 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -1326,6 +1326,9 @@ static ssize_t pagemap_read(struct file *file, char __user *buf, static int pagemap_open(struct inode *inode, struct file *file) { + /* do not disclose physical addresses: attack vector */ + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; pr_warn_once("Bits 55-60 of /proc/PID/pagemap entries are about " "to stop being page-shift some time soon. See the " "linux/Documentation/vm/pagemap.txt for details.\n"); From 637d00ad269248c6780eb6b1fb883b6aba326c25 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 26 Feb 2015 07:22:05 +0000 Subject: [PATCH 455/788] crypto: arm/aes update NEON AES module to latest OpenSSL version commit 001eabfd54c0cbf9d7d16264ddc8cc0bee67e3ed upstream. This updates the bit sliced AES module to the latest version in the upstream OpenSSL repository (e620e5ae37bc). This is needed to fix a bug in the XTS decryption path, where data chunked in a certain way could trigger the ciphertext stealing code, which is not supposed to be active in the kernel build (The kernel implementation of XTS only supports round multiples of the AES block size of 16 bytes, whereas the conformant OpenSSL implementation of XTS supports inputs of arbitrary size by applying ciphertext stealing). This is fixed in the upstream version by adding the missing #ifndef XTS_CHAIN_TWEAK around the offending instructions. The upstream code also contains the change applied by Russell to build the code unconditionally, i.e., even if __LINUX_ARM_ARCH__ < 7, but implemented slightly differently. Fixes: e4e7f10bfc40 ("ARM: add support for bit sliced AES using NEON instructions") Reported-by: Adrian Kotelba Signed-off-by: Ard Biesheuvel Tested-by: Milan Broz Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- arch/arm/crypto/aesbs-core.S_shipped | 12 ++++++++---- arch/arm/crypto/bsaes-armv7.pl | 12 ++++++++---- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/arch/arm/crypto/aesbs-core.S_shipped b/arch/arm/crypto/aesbs-core.S_shipped index 71e5fc7cfb18f4..1d1800f71c5b3d 100644 --- a/arch/arm/crypto/aesbs-core.S_shipped +++ b/arch/arm/crypto/aesbs-core.S_shipped @@ -58,14 +58,18 @@ # define VFP_ABI_FRAME 0 # define BSAES_ASM_EXTENDED_KEY # define XTS_CHAIN_TWEAK -# define __ARM_ARCH__ 7 +# define __ARM_ARCH__ __LINUX_ARM_ARCH__ +# define __ARM_MAX_ARCH__ 7 #endif #ifdef __thumb__ # define adrl adr #endif -#if __ARM_ARCH__>=7 +#if __ARM_MAX_ARCH__>=7 +.arch armv7-a +.fpu neon + .text .syntax unified @ ARMv7-capable assembler is expected to handle this #ifdef __thumb2__ @@ -74,8 +78,6 @@ .code 32 #endif -.fpu neon - .type _bsaes_decrypt8,%function .align 4 _bsaes_decrypt8: @@ -2095,9 +2097,11 @@ bsaes_xts_decrypt: vld1.8 {q8}, [r0] @ initial tweak adr r2, .Lxts_magic +#ifndef XTS_CHAIN_TWEAK tst r9, #0xf @ if not multiple of 16 it ne @ Thumb2 thing, sanity check in ARM subne r9, #0x10 @ subtract another 16 bytes +#endif subs r9, #0x80 blo .Lxts_dec_short diff --git a/arch/arm/crypto/bsaes-armv7.pl b/arch/arm/crypto/bsaes-armv7.pl index be068db960ee00..a4d3856e7d2477 100644 --- a/arch/arm/crypto/bsaes-armv7.pl +++ b/arch/arm/crypto/bsaes-armv7.pl @@ -701,14 +701,18 @@ sub bitslice { # define VFP_ABI_FRAME 0 # define BSAES_ASM_EXTENDED_KEY # define XTS_CHAIN_TWEAK -# define __ARM_ARCH__ 7 +# define __ARM_ARCH__ __LINUX_ARM_ARCH__ +# define __ARM_MAX_ARCH__ 7 #endif #ifdef __thumb__ # define adrl adr #endif -#if __ARM_ARCH__>=7 +#if __ARM_MAX_ARCH__>=7 +.arch armv7-a +.fpu neon + .text .syntax unified @ ARMv7-capable assembler is expected to handle this #ifdef __thumb2__ @@ -717,8 +721,6 @@ sub bitslice { .code 32 #endif -.fpu neon - .type _bsaes_decrypt8,%function .align 4 _bsaes_decrypt8: @@ -2076,9 +2078,11 @@ sub bitslice_key { vld1.8 {@XMM[8]}, [r0] @ initial tweak adr $magic, .Lxts_magic +#ifndef XTS_CHAIN_TWEAK tst $len, #0xf @ if not multiple of 16 it ne @ Thumb2 thing, sanity check in ARM subne $len, #0x10 @ subtract another 16 bytes +#endif subs $len, #0x80 blo .Lxts_dec_short From b90935f1d9a06d18bd7f36973556e7941104ca9a Mon Sep 17 00:00:00 2001 From: Stephan Mueller Date: Thu, 12 Mar 2015 09:17:51 +0100 Subject: [PATCH 456/788] crypto: aesni - fix memory usage in GCM decryption commit ccfe8c3f7e52ae83155cb038753f4c75b774ca8a upstream. The kernel crypto API logic requires the caller to provide the length of (ciphertext || authentication tag) as cryptlen for the AEAD decryption operation. Thus, the cipher implementation must calculate the size of the plaintext output itself and cannot simply use cryptlen. The RFC4106 GCM decryption operation tries to overwrite cryptlen memory in req->dst. As the destination buffer for decryption only needs to hold the plaintext memory but cryptlen references the input buffer holding (ciphertext || authentication tag), the assumption of the destination buffer length in RFC4106 GCM operation leads to a too large size. This patch simply uses the already calculated plaintext size. In addition, this patch fixes the offset calculation of the AAD buffer pointer: as mentioned before, cryptlen already includes the size of the tag. Thus, the tag does not need to be added. With the addition, the AAD will be written beyond the already allocated buffer. Note, this fixes a kernel crash that can be triggered from user space via AF_ALG(aead) -- simply use the libkcapi test application from [1] and update it to use rfc4106-gcm-aes. Using [1], the changes were tested using CAVS vectors to demonstrate that the crypto operation still delivers the right results. [1] http://www.chronox.de/libkcapi.html CC: Tadeusz Struk Signed-off-by: Stephan Mueller Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- arch/x86/crypto/aesni-intel_glue.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/crypto/aesni-intel_glue.c b/arch/x86/crypto/aesni-intel_glue.c index ae855f4f64b775..95ad7adeb5fbea 100644 --- a/arch/x86/crypto/aesni-intel_glue.c +++ b/arch/x86/crypto/aesni-intel_glue.c @@ -1133,7 +1133,7 @@ static int __driver_rfc4106_decrypt(struct aead_request *req) src = kmalloc(req->cryptlen + req->assoclen, GFP_ATOMIC); if (!src) return -ENOMEM; - assoc = (src + req->cryptlen + auth_tag_len); + assoc = (src + req->cryptlen); scatterwalk_map_and_copy(src, req->src, 0, req->cryptlen, 0); scatterwalk_map_and_copy(assoc, req->assoc, 0, req->assoclen, 0); @@ -1158,7 +1158,7 @@ static int __driver_rfc4106_decrypt(struct aead_request *req) scatterwalk_done(&src_sg_walk, 0, 0); scatterwalk_done(&assoc_sg_walk, 0, 0); } else { - scatterwalk_map_and_copy(dst, req->dst, 0, req->cryptlen, 1); + scatterwalk_map_and_copy(dst, req->dst, 0, tempCipherLen, 1); kfree(src); } return retval; From 4e4120654b4196c2f252dcdb5e974f16816c2939 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 13 Mar 2015 09:53:09 +0100 Subject: [PATCH 457/788] x86/fpu: Avoid math_state_restore() without used_math() in __restore_xstate_sig() commit a7c80ebcac3068b1c3cb27d538d29558c30010c8 upstream. math_state_restore() assumes it is called with irqs disabled, but this is not true if the caller is __restore_xstate_sig(). This means that if ia32_fxstate == T and __copy_from_user() fails, __restore_xstate_sig() returns with irqs disabled too. This triggers: BUG: sleeping function called from invalid context at kernel/locking/rwsem.c:41 dump_stack ___might_sleep ? _raw_spin_unlock_irqrestore __might_sleep down_read ? _raw_spin_unlock_irqrestore print_vma_addr signal_fault sys32_rt_sigreturn Change __restore_xstate_sig() to call set_used_math() unconditionally. This avoids enabling and disabling interrupts in math_state_restore(). If copy_from_user() fails, we can simply do fpu_finit() by hand. [ Note: this is only the first step. math_state_restore() should not check used_math(), it should set this flag. While init_fpu() should simply die. ] Signed-off-by: Oleg Nesterov Signed-off-by: Borislav Petkov Cc: Andy Lutomirski Cc: Borislav Petkov Cc: Dave Hansen Cc: Fenghua Yu Cc: H. Peter Anvin Cc: Linus Torvalds Cc: Pekka Riikonen Cc: Quentin Casasnovas Cc: Rik van Riel Cc: Suresh Siddha Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/20150307153844.GB25954@redhat.com Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/xsave.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/x86/kernel/xsave.c b/arch/x86/kernel/xsave.c index 0de1fae2bdf000..8be1e1711203da 100644 --- a/arch/x86/kernel/xsave.c +++ b/arch/x86/kernel/xsave.c @@ -378,7 +378,7 @@ int __restore_xstate_sig(void __user *buf, void __user *buf_fx, int size) * thread's fpu state, reconstruct fxstate from the fsave * header. Sanitize the copied state etc. */ - struct xsave_struct *xsave = &tsk->thread.fpu.state->xsave; + struct fpu *fpu = &tsk->thread.fpu; struct user_i387_ia32_struct env; int err = 0; @@ -392,14 +392,15 @@ int __restore_xstate_sig(void __user *buf, void __user *buf_fx, int size) */ drop_fpu(tsk); - if (__copy_from_user(xsave, buf_fx, state_size) || + if (__copy_from_user(&fpu->state->xsave, buf_fx, state_size) || __copy_from_user(&env, buf, sizeof(env))) { + fpu_finit(fpu); err = -1; } else { sanitize_restored_xstate(tsk, &env, xstate_bv, fx_only); - set_used_math(); } + set_used_math(); if (use_eager_fpu()) { preempt_disable(); math_state_restore(); From 4acde8bf03d0aeb901cf46c4babefb45aec92f76 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 13 Mar 2015 09:53:10 +0100 Subject: [PATCH 458/788] x86/fpu: Drop_fpu() should not assume that tsk equals current commit f4c3686386393c120710dd34df2a74183ab805fd upstream. drop_fpu() does clear_used_math() and usually this is correct because tsk == current. However switch_fpu_finish()->restore_fpu_checking() is called before __switch_to() updates the "current_task" variable. If it fails, we will wrongly clear the PF_USED_MATH flag of the previous task. So use clear_stopped_child_used_math() instead. Signed-off-by: Oleg Nesterov Signed-off-by: Borislav Petkov Reviewed-by: Rik van Riel Cc: Andy Lutomirski Cc: Borislav Petkov Cc: Dave Hansen Cc: Fenghua Yu Cc: H. Peter Anvin Cc: Linus Torvalds Cc: Pekka Riikonen Cc: Quentin Casasnovas Cc: Suresh Siddha Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/20150309171041.GB11388@redhat.com Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/fpu-internal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/include/asm/fpu-internal.h b/arch/x86/include/asm/fpu-internal.h index e97622f577229e..f895358db0abe4 100644 --- a/arch/x86/include/asm/fpu-internal.h +++ b/arch/x86/include/asm/fpu-internal.h @@ -368,7 +368,7 @@ static inline void drop_fpu(struct task_struct *tsk) preempt_disable(); tsk->thread.fpu_counter = 0; __drop_fpu(tsk); - clear_used_math(); + clear_stopped_child_used_math(tsk); preempt_enable(); } From 506be1c17d1d454b1f25e01a027cd2d1338f6d89 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 5 Mar 2015 11:54:46 +0100 Subject: [PATCH 459/788] kvm: move advertising of KVM_CAP_IRQFD to common code commit dc9be0fac70a2ad86e31a81372bb0bdfb6945353 upstream. POWER supports irqfds but forgot to advertise them. Some userspace does not check for the capability, but others check it---thus they work on x86 and s390 but not POWER. To avoid that other architectures in the future make the same mistake, let common code handle KVM_CAP_IRQFD the same way as KVM_CAP_IRQFD_RESAMPLE. Reported-and-tested-by: Greg Kurz Fixes: 297e21053a52f060944e9f0de4c64fad9bcd72fc Signed-off-by: Paolo Bonzini Signed-off-by: Marcelo Tosatti Signed-off-by: Greg Kroah-Hartman --- arch/s390/kvm/kvm-s390.c | 1 - arch/x86/kvm/x86.c | 1 - virt/kvm/kvm_main.c | 1 + 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 9af01dc966d073..e3bab3a0da98d5 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -159,7 +159,6 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) case KVM_CAP_ONE_REG: case KVM_CAP_ENABLE_CAP: case KVM_CAP_S390_CSS_SUPPORT: - case KVM_CAP_IRQFD: case KVM_CAP_IOEVENTFD: case KVM_CAP_DEVICE_CTRL: case KVM_CAP_ENABLE_CAP_VM: diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index c259814200bd34..64d76c1022306c 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -2716,7 +2716,6 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) case KVM_CAP_USER_NMI: case KVM_CAP_REINJECT_CONTROL: case KVM_CAP_IRQ_INJECT_STATUS: - case KVM_CAP_IRQFD: case KVM_CAP_IOEVENTFD: case KVM_CAP_IOEVENTFD_NO_LENGTH: case KVM_CAP_PIT2: diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 1cc6e2e1998270..ec83b11c597844 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -2416,6 +2416,7 @@ static long kvm_vm_ioctl_check_extension_generic(struct kvm *kvm, long arg) case KVM_CAP_SIGNAL_MSI: #endif #ifdef CONFIG_HAVE_KVM_IRQFD + case KVM_CAP_IRQFD: case KVM_CAP_IRQFD_RESAMPLE: #endif case KVM_CAP_CHECK_EXTENSION_VM: From 427c08dcbc5600c017c1d9d2232d98b78ea982ac Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 5 Mar 2015 09:13:31 +0100 Subject: [PATCH 460/788] x86/vdso: Fix the build on GCC5 commit e893286918d2cde3a94850d8f7101cd1039e0c62 upstream. On gcc5 the kernel does not link: ld: .eh_frame_hdr table[4] FDE at 0000000000000648 overlaps table[5] FDE at 0000000000000670. Because prior GCC versions always emitted NOPs on ALIGN directives, but gcc5 started omitting them. .LSTARTFDEDLSI1 says: /* HACK: The dwarf2 unwind routines will subtract 1 from the return address to get an address in the middle of the presumed call instruction. Since we didn't get here via a call, we need to include the nop before the real start to make up for it. */ .long .LSTART_sigreturn-1-. /* PC-relative start address */ But commit 69d0627a7f6e ("x86 vDSO: reorder vdso32 code") from 2.6.25 replaced .org __kernel_vsyscall+32,0x90 by ALIGN right before __kernel_sigreturn. Of course, ALIGN need not generate any NOP in there. Esp. gcc5 collapses vclock_gettime.o and int80.o together with no generated NOPs as "ALIGN". So fix this by adding to that point at least a single NOP and make the function ALIGN possibly with more NOPs then. Kudos for reporting and diagnosing should go to Richard. Reported-by: Richard Biener Signed-off-by: Jiri Slaby Acked-by: Andy Lutomirski Cc: Borislav Petkov Cc: H. Peter Anvin Cc: Linus Torvalds Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/1425543211-12542-1-git-send-email-jslaby@suse.cz Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/vdso/vdso32/sigreturn.S | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/vdso/vdso32/sigreturn.S b/arch/x86/vdso/vdso32/sigreturn.S index 31776d0efc8c40..d7ec4e251c0a2e 100644 --- a/arch/x86/vdso/vdso32/sigreturn.S +++ b/arch/x86/vdso/vdso32/sigreturn.S @@ -17,6 +17,7 @@ .text .globl __kernel_sigreturn .type __kernel_sigreturn,@function + nop /* this guy is needed for .LSTARTFDEDLSI1 below (watch for HACK) */ ALIGN __kernel_sigreturn: .LSTART_sigreturn: From b8a148e212e5dd1549bb1f7652ad041090702ea4 Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Mon, 9 Mar 2015 17:42:31 -0700 Subject: [PATCH 461/788] x86/asm/entry/32: Fix user_mode() misuses commit 394838c96013ba414a24ffe7a2a593a9154daadf upstream. The one in do_debug() is probably harmless, but better safe than sorry. Signed-off-by: Andy Lutomirski Cc: Borislav Petkov Cc: Dave Hansen Cc: H. Peter Anvin Cc: Linus Torvalds Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/d67deaa9df5458363623001f252d1aee3215d014.1425948056.git.luto@amacapital.net Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/traps.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 88900e288021f2..89f4e64c2ceaef 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -300,7 +300,7 @@ dotraplinkage void do_bounds(struct pt_regs *regs, long error_code) goto exit; conditional_sti(regs); - if (!user_mode(regs)) + if (!user_mode_vm(regs)) die("bounds", regs, error_code); if (!cpu_feature_enabled(X86_FEATURE_MPX)) { @@ -566,7 +566,7 @@ dotraplinkage void do_debug(struct pt_regs *regs, long error_code) * then it's very likely the result of an icebp/int01 trap. * User wants a sigtrap for that. */ - if (!dr6 && user_mode(regs)) + if (!dr6 && user_mode_vm(regs)) user_icebp = 1; /* Catch kmemcheck conditions first of all! */ From b93ee0365daeee709bb4e6f7553f90868fb40852 Mon Sep 17 00:00:00 2001 From: Daniel J Blueman Date: Thu, 12 Mar 2015 16:55:13 +0100 Subject: [PATCH 462/788] x86/apic/numachip: Fix sibling map with NumaChip commit c8a470cab030bae8f9e6e5cfff72b047b7c627a7 upstream. On NumaChip systems, the physical processor ID assignment wasn't accounting for the number of nodes in AMD multi-module processors, giving an incorrect sibling map: $ cd /sys/devices/system/cpu/cpu29/topology $ grep . * core_id:5 core_siblings:00000000,ff000000 core_siblings_list:24-31 physical_package_id:3 thread_siblings:00000000,30000000 thread_siblings_list:28-29 This fixes it: $ cd /sys/devices/system/cpu/cpu29/topology $ grep . * core_id:5 core_siblings:00000000,ffff0000 core_siblings_list:16-31 physical_package_id:1 thread_siblings:00000000,30000000 thread_siblings_list:28-29 Signed-off-by: Daniel J Blueman Signed-off-by: Borislav Petkov Cc: H. Peter Anvin Cc: Steffen Persvold Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/1426135950-10110-1-git-send-email-daniel@numascale.com Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/apic/apic_numachip.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/arch/x86/kernel/apic/apic_numachip.c b/arch/x86/kernel/apic/apic_numachip.c index c2fd21fed00284..017149cded0760 100644 --- a/arch/x86/kernel/apic/apic_numachip.c +++ b/arch/x86/kernel/apic/apic_numachip.c @@ -37,10 +37,12 @@ static const struct apic apic_numachip; static unsigned int get_apic_id(unsigned long x) { unsigned long value; - unsigned int id; + unsigned int id = (x >> 24) & 0xff; - rdmsrl(MSR_FAM10H_NODE_ID, value); - id = ((x >> 24) & 0xffU) | ((value << 2) & 0xff00U); + if (static_cpu_has_safe(X86_FEATURE_NODEID_MSR)) { + rdmsrl(MSR_FAM10H_NODE_ID, value); + id |= (value << 2) & 0xff00; + } return id; } @@ -155,10 +157,18 @@ static int __init numachip_probe(void) static void fixup_cpu_id(struct cpuinfo_x86 *c, int node) { - if (c->phys_proc_id != node) { - c->phys_proc_id = node; - per_cpu(cpu_llc_id, smp_processor_id()) = node; + u64 val; + u32 nodes = 1; + + this_cpu_write(cpu_llc_id, node); + + /* Account for nodes per socket in multi-core-module processors */ + if (static_cpu_has_safe(X86_FEATURE_NODEID_MSR)) { + rdmsrl(MSR_FAM10H_NODE_ID, val); + nodes = ((val >> 3) & 7) + 1; } + + c->phys_proc_id = node / nodes; } static int __init numachip_system_init(void) From 915b529c7ee0d876f27a97ef83ed62039e2ade43 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Tue, 24 Feb 2015 17:58:02 +1100 Subject: [PATCH 463/788] powerpc/smp: Wait until secondaries are active & online commit 875ebe940d77a41682c367ad799b4f39f128d3fa upstream. Anton has a busy ppc64le KVM box where guests sometimes hit the infamous "kernel BUG at kernel/smpboot.c:134!" issue during boot: BUG_ON(td->cpu != smp_processor_id()); Basically a per CPU hotplug thread scheduled on the wrong CPU. The oops output confirms it: CPU: 0 Comm: watchdog/130 The problem is that we aren't ensuring the CPU active bit is set for the secondary before allowing the master to continue on. The master unparks the secondary CPU's kthreads and the scheduler looks for a CPU to run on. It calls select_task_rq() and realises the suggested CPU is not in the cpus_allowed mask. It then ends up in select_fallback_rq(), and since the active bit isnt't set we choose some other CPU to run on. This seems to have been introduced by 6acbfb96976f "sched: Fix hotplug vs. set_cpus_allowed_ptr()", which changed from setting active before online to setting active after online. However that was in turn fixing a bug where other code assumed an active CPU was also online, so we can't just revert that fix. The simplest fix is just to spin waiting for both active & online to be set. We already have a barrier prior to set_cpu_online() (which also sets active), to ensure all other setup is completed before online & active are set. Fixes: 6acbfb96976f ("sched: Fix hotplug vs. set_cpus_allowed_ptr()") Signed-off-by: Michael Ellerman Signed-off-by: Anton Blanchard Signed-off-by: Michael Ellerman Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/kernel/smp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c index 8b2d2dc8ef106e..c68cc9dd28fb68 100644 --- a/arch/powerpc/kernel/smp.c +++ b/arch/powerpc/kernel/smp.c @@ -555,8 +555,8 @@ int __cpu_up(unsigned int cpu, struct task_struct *tidle) if (smp_ops->give_timebase) smp_ops->give_timebase(); - /* Wait until cpu puts itself in the online map */ - while (!cpu_online(cpu)) + /* Wait until cpu puts itself in the online & active maps */ + while (!cpu_online(cpu) || !cpu_active(cpu)) cpu_relax(); return 0; From c011268dc6393db38eef439a9849511e1d885b2a Mon Sep 17 00:00:00 2001 From: Nishanth Aravamudan Date: Sat, 21 Feb 2015 11:00:50 -0800 Subject: [PATCH 464/788] powerpc/iommu: Remove IOMMU device references via bus notifier commit 4ad04e5987115ece5fa8a0cf1dc72fcd4707e33e upstream. After d905c5df9aef ("PPC: POWERNV: move iommu_add_device earlier"), the refcnt on the kobject backing the IOMMU group for a PCI device is elevated by each call to pci_dma_dev_setup_pSeriesLP() (via set_iommu_table_base_and_group). When we go to dlpar a multi-function PCI device out: iommu_reconfig_notifier -> iommu_free_table -> iommu_group_put BUG_ON(tbl->it_group) We trip this BUG_ON, because there are still references on the table, so it is not freed. Fix this by moving the powernv bus notifier to common code and calling it for both powernv and pseries. Fixes: d905c5df9aef ("PPC: POWERNV: move iommu_add_device earlier") Signed-off-by: Nishanth Aravamudan Tested-by: Nishanth Aravamudan Signed-off-by: Michael Ellerman Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/include/asm/iommu.h | 6 ++++++ arch/powerpc/kernel/iommu.c | 26 ++++++++++++++++++++++++++ arch/powerpc/platforms/powernv/pci.c | 26 -------------------------- arch/powerpc/platforms/pseries/iommu.c | 2 ++ 4 files changed, 34 insertions(+), 26 deletions(-) diff --git a/arch/powerpc/include/asm/iommu.h b/arch/powerpc/include/asm/iommu.h index 9cfa3706a1b875..f1ea5972f6eccd 100644 --- a/arch/powerpc/include/asm/iommu.h +++ b/arch/powerpc/include/asm/iommu.h @@ -113,6 +113,7 @@ extern void iommu_register_group(struct iommu_table *tbl, int pci_domain_number, unsigned long pe_num); extern int iommu_add_device(struct device *dev); extern void iommu_del_device(struct device *dev); +extern int __init tce_iommu_bus_notifier_init(void); #else static inline void iommu_register_group(struct iommu_table *tbl, int pci_domain_number, @@ -128,6 +129,11 @@ static inline int iommu_add_device(struct device *dev) static inline void iommu_del_device(struct device *dev) { } + +static inline int __init tce_iommu_bus_notifier_init(void) +{ + return 0; +} #endif /* !CONFIG_IOMMU_API */ static inline void set_iommu_table_base_and_group(struct device *dev, diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c index 5d3968c4d79973..b054f33ab1fbcd 100644 --- a/arch/powerpc/kernel/iommu.c +++ b/arch/powerpc/kernel/iommu.c @@ -1175,4 +1175,30 @@ void iommu_del_device(struct device *dev) } EXPORT_SYMBOL_GPL(iommu_del_device); +static int tce_iommu_bus_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct device *dev = data; + + switch (action) { + case BUS_NOTIFY_ADD_DEVICE: + return iommu_add_device(dev); + case BUS_NOTIFY_DEL_DEVICE: + if (dev->iommu_group) + iommu_del_device(dev); + return 0; + default: + return 0; + } +} + +static struct notifier_block tce_iommu_bus_nb = { + .notifier_call = tce_iommu_bus_notifier, +}; + +int __init tce_iommu_bus_notifier_init(void) +{ + bus_register_notifier(&pci_bus_type, &tce_iommu_bus_nb); + return 0; +} #endif /* CONFIG_IOMMU_API */ diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c index 4945e87f12dca5..3948b8a868f26c 100644 --- a/arch/powerpc/platforms/powernv/pci.c +++ b/arch/powerpc/platforms/powernv/pci.c @@ -866,30 +866,4 @@ void __init pnv_pci_init(void) #endif } -static int tce_iommu_bus_notifier(struct notifier_block *nb, - unsigned long action, void *data) -{ - struct device *dev = data; - - switch (action) { - case BUS_NOTIFY_ADD_DEVICE: - return iommu_add_device(dev); - case BUS_NOTIFY_DEL_DEVICE: - if (dev->iommu_group) - iommu_del_device(dev); - return 0; - default: - return 0; - } -} - -static struct notifier_block tce_iommu_bus_nb = { - .notifier_call = tce_iommu_bus_notifier, -}; - -static int __init tce_iommu_bus_notifier_init(void) -{ - bus_register_notifier(&pci_bus_type, &tce_iommu_bus_nb); - return 0; -} machine_subsys_initcall_sync(powernv, tce_iommu_bus_notifier_init); diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index 1d3d52dc3ff31e..7803a19adb3182 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -1340,3 +1340,5 @@ static int __init disable_multitce(char *str) } __setup("multitce=", disable_multitce); + +machine_subsys_initcall_sync(pseries, tce_iommu_bus_notifier_init); From 126f113c93624e5939d213846105d21a64830847 Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Sat, 21 Feb 2015 21:03:10 +0200 Subject: [PATCH 465/788] ipvs: add missing ip_vs_pe_put in sync code commit 528c943f3bb919aef75ab2fff4f00176f09a4019 upstream. ip_vs_conn_fill_param_sync() gets in param.pe a module reference for persistence engine from __ip_vs_pe_getbyname() but forgets to put it. Problem occurs in backup for sync protocol v1 (2.6.39). Also, pe_data usually comes in sync messages for connection templates and ip_vs_conn_new() copies the pointer only in this case. Make sure pe_data is not leaked if it comes unexpectedly for normal connections. Leak can happen only if bogus messages are sent to backup server. Fixes: fe5e7a1efb66 ("IPVS: Backup, Adding Version 1 receive capability") Signed-off-by: Julian Anastasov Signed-off-by: Simon Horman Signed-off-by: Greg Kroah-Hartman --- net/netfilter/ipvs/ip_vs_sync.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index c47ffd7a0a709c..d93ceeb3ef0482 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -896,6 +896,8 @@ static void ip_vs_proc_conn(struct net *net, struct ip_vs_conn_param *param, IP_VS_DBG(2, "BACKUP, add new conn. failed\n"); return; } + if (!(flags & IP_VS_CONN_F_TEMPLATE)) + kfree(param->pe_data); } if (opt) @@ -1169,6 +1171,7 @@ static inline int ip_vs_proc_sync_conn(struct net *net, __u8 *p, __u8 *msg_end) (opt_flags & IPVS_OPT_F_SEQ_DATA ? &opt : NULL) ); #endif + ip_vs_pe_put(param.pe); return 0; /* Error exit */ out: From 2a4e9ebd1100ad8fa52b0a4e7775b614b7733690 Mon Sep 17 00:00:00 2001 From: Alexey Andriyanov Date: Fri, 6 Feb 2015 22:32:20 +0300 Subject: [PATCH 466/788] ipvs: fix inability to remove a mixed-family RS commit dd3733b3e798daf778a1ec08557f388f00fdc2f6 upstream. The current code prevents any operation with a mixed-family dest unless IP_VS_CONN_F_TUNNEL flag is set. The problem is that it's impossible for the client to follow this rule, because ip_vs_genl_parse_dest does not even read the destination conn_flags when cmd = IPVS_CMD_DEL_DEST (need_full_dest = 0). Also, not every client can pass this flag when removing a dest. ipvsadm, for example, does not support the "-i" command line option together with the "-d" option. This change disables any checks for mixed-family on IPVS_CMD_DEL_DEST command. Signed-off-by: Alexey Andriyanov Fixes: bc18d37f676f ("ipvs: Allow heterogeneous pools now that we support them") Acked-by: Julian Anastasov Signed-off-by: Simon Horman Signed-off-by: Greg Kroah-Hartman --- net/netfilter/ipvs/ip_vs_ctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index b8295a430a5600..fdcda8be1f0f8f 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -3399,7 +3399,7 @@ static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info) if (udest.af == 0) udest.af = svc->af; - if (udest.af != svc->af) { + if (udest.af != svc->af && cmd != IPVS_CMD_DEL_DEST) { /* The synchronization protocol is incompatible * with mixed family services */ From 29d2b670b700452639f75e7cb433cd1106088cd3 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 12 Feb 2015 22:15:31 +0100 Subject: [PATCH 467/788] netfilter: nft_compat: fix module refcount underflow commit 520aa7414bb590f39d0d1591b06018e60cbc7cf4 upstream. Feb 12 18:20:42 nfdev kernel: ------------[ cut here ]------------ Feb 12 18:20:42 nfdev kernel: WARNING: CPU: 4 PID: 4359 at kernel/module.c:963 module_put+0x9b/0xba() Feb 12 18:20:42 nfdev kernel: CPU: 4 PID: 4359 Comm: ebtables-compat Tainted: G W 3.19.0-rc6+ #43 [...] Feb 12 18:20:42 nfdev kernel: Call Trace: Feb 12 18:20:42 nfdev kernel: [] dump_stack+0x4c/0x65 Feb 12 18:20:42 nfdev kernel: [] warn_slowpath_common+0x9c/0xb6 Feb 12 18:20:42 nfdev kernel: [] ? module_put+0x9b/0xba Feb 12 18:20:42 nfdev kernel: [] warn_slowpath_null+0x15/0x17 Feb 12 18:20:42 nfdev kernel: [] module_put+0x9b/0xba Feb 12 18:20:42 nfdev kernel: [] nft_match_destroy+0x45/0x4c Feb 12 18:20:42 nfdev kernel: [] nf_tables_rule_destroy+0x28/0x70 Reported-by: Arturo Borrero Gonzalez Signed-off-by: Pablo Neira Ayuso Tested-by: Arturo Borrero Gonzalez Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nft_compat.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c index 265e190f22187d..b6364869c2e098 100644 --- a/net/netfilter/nft_compat.c +++ b/net/netfilter/nft_compat.c @@ -578,8 +578,12 @@ nft_match_select_ops(const struct nft_ctx *ctx, struct xt_match *match = nft_match->ops.data; if (strcmp(match->name, mt_name) == 0 && - match->revision == rev && match->family == family) + match->revision == rev && match->family == family) { + if (!try_module_get(match->me)) + return ERR_PTR(-ENOENT); + return &nft_match->ops; + } } match = xt_request_find_match(family, mt_name, rev); @@ -648,8 +652,12 @@ nft_target_select_ops(const struct nft_ctx *ctx, struct xt_target *target = nft_target->ops.data; if (strcmp(target->name, tg_name) == 0 && - target->revision == rev && target->family == family) + target->revision == rev && target->family == family) { + if (!try_module_get(target->me)) + return ERR_PTR(-ENOENT); + return &nft_target->ops; + } } target = xt_request_find_target(family, tg_name, rev); From 680f3aca7b3846f6ae1a2875f637d00b3cdfa954 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 15 Feb 2015 19:03:45 -0800 Subject: [PATCH 468/788] netfilter: xt_socket: fix a stack corruption bug commit 78296c97ca1fd3b104f12e1f1fbc06c46635990b upstream. As soon as extract_icmp6_fields() returns, its local storage (automatic variables) is deallocated and can be overwritten. Lets add an additional parameter to make sure storage is valid long enough. While we are at it, adds some const qualifiers. Signed-off-by: Eric Dumazet Fixes: b64c9256a9b76 ("tproxy: added IPv6 support to the socket match") Signed-off-by: Pablo Neira Ayuso Signed-off-by: Greg Kroah-Hartman --- net/netfilter/xt_socket.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c index 1ba67931eb1b16..13332dbf291d6e 100644 --- a/net/netfilter/xt_socket.c +++ b/net/netfilter/xt_socket.c @@ -243,12 +243,13 @@ static int extract_icmp6_fields(const struct sk_buff *skb, unsigned int outside_hdrlen, int *protocol, - struct in6_addr **raddr, - struct in6_addr **laddr, + const struct in6_addr **raddr, + const struct in6_addr **laddr, __be16 *rport, - __be16 *lport) + __be16 *lport, + struct ipv6hdr *ipv6_var) { - struct ipv6hdr *inside_iph, _inside_iph; + const struct ipv6hdr *inside_iph; struct icmp6hdr *icmph, _icmph; __be16 *ports, _ports[2]; u8 inside_nexthdr; @@ -263,12 +264,14 @@ extract_icmp6_fields(const struct sk_buff *skb, if (icmph->icmp6_type & ICMPV6_INFOMSG_MASK) return 1; - inside_iph = skb_header_pointer(skb, outside_hdrlen + sizeof(_icmph), sizeof(_inside_iph), &_inside_iph); + inside_iph = skb_header_pointer(skb, outside_hdrlen + sizeof(_icmph), + sizeof(*ipv6_var), ipv6_var); if (inside_iph == NULL) return 1; inside_nexthdr = inside_iph->nexthdr; - inside_hdrlen = ipv6_skip_exthdr(skb, outside_hdrlen + sizeof(_icmph) + sizeof(_inside_iph), + inside_hdrlen = ipv6_skip_exthdr(skb, outside_hdrlen + sizeof(_icmph) + + sizeof(*ipv6_var), &inside_nexthdr, &inside_fragoff); if (inside_hdrlen < 0) return 1; /* hjm: Packet has no/incomplete transport layer headers. */ @@ -315,10 +318,10 @@ xt_socket_get_sock_v6(struct net *net, const u8 protocol, static bool socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par) { - struct ipv6hdr *iph = ipv6_hdr(skb); + struct ipv6hdr ipv6_var, *iph = ipv6_hdr(skb); struct udphdr _hdr, *hp = NULL; struct sock *sk = skb->sk; - struct in6_addr *daddr = NULL, *saddr = NULL; + const struct in6_addr *daddr = NULL, *saddr = NULL; __be16 uninitialized_var(dport), uninitialized_var(sport); int thoff = 0, uninitialized_var(tproto); const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo; @@ -342,7 +345,7 @@ socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par) } else if (tproto == IPPROTO_ICMPV6) { if (extract_icmp6_fields(skb, thoff, &tproto, &saddr, &daddr, - &sport, &dport)) + &sport, &dport, &ipv6_var)) return false; } else { return false; From 0a12e44c18d25ba0b796cc49610430cbfd4676ec Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 3 Mar 2015 20:04:18 +0000 Subject: [PATCH 469/788] netfilter: nf_tables: fix transaction race condition commit 8670c3a55e91cb27a4b4d4d4c4fa35b0149e1abf upstream. A race condition exists in the rule transaction code for rules that get added and removed within the same transaction. The new rule starts out as inactive in the current and active in the next generation and is inserted into the ruleset. When it is deleted, it is additionally set to inactive in the next generation as well. On commit the next generation is begun, then the actions are finalized. For the new rule this would mean clearing out the inactive bit for the previously current, now next generation. However nft_rule_clear() clears out the bits for *both* generations, activating the rule in the current generation, where it should be deactivated due to being deleted. The rule will thus be active until the deletion is finalized, removing the rule from the ruleset. Similarly, when aborting a transaction for the same case, the undo of insertion will remove it from the RCU protected rule list, the deletion will clear out all bits. However until the next RCU synchronization after all operations have been undone, the rule is active on CPUs which can still see the rule on the list. Generally, there may never be any modifications of the current generations' inactive bit since this defeats the entire purpose of atomicity. Change nft_rule_clear() to only touch the next generations bit to fix this. Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nf_tables_api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 1ff04bcd487154..05380df5447418 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -227,7 +227,7 @@ nft_rule_deactivate_next(struct net *net, struct nft_rule *rule) static inline void nft_rule_clear(struct net *net, struct nft_rule *rule) { - rule->genmask = 0; + rule->genmask &= ~(1 << gencursor_next(net)); } static int From 8f246228ca057df31225216063e198b25cea7a59 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 20 Feb 2015 17:11:10 +0100 Subject: [PATCH 470/788] netfilter: nf_tables: fix addition/deletion of elements from commit/abort commit 02263db00b6cb98701332aa257c07ca549c2324b upstream. We have several problems in this path: 1) There is a use-after-free when removing individual elements from the commit path. 2) We have to uninit() the data part of the element from the abort path to avoid a chain refcount leak. 3) We have to check for set->flags to see if there's a mapping, instead of the element flags. 4) We have to check for !(flags & NFT_SET_ELEM_INTERVAL_END) to skip elements that are part of the interval that have no data part, so they don't need to be uninit(). Signed-off-by: Pablo Neira Ayuso Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nf_tables_api.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 05380df5447418..f77f4cc8e3e968 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -3606,12 +3606,11 @@ static int nf_tables_commit(struct sk_buff *skb) &te->elem, NFT_MSG_DELSETELEM, 0); te->set->ops->get(te->set, &te->elem); - te->set->ops->remove(te->set, &te->elem); nft_data_uninit(&te->elem.key, NFT_DATA_VALUE); - if (te->elem.flags & NFT_SET_MAP) { - nft_data_uninit(&te->elem.data, - te->set->dtype); - } + if (te->set->flags & NFT_SET_MAP && + !(te->elem.flags & NFT_SET_ELEM_INTERVAL_END)) + nft_data_uninit(&te->elem.data, te->set->dtype); + te->set->ops->remove(te->set, &te->elem); nft_trans_destroy(trans); break; } @@ -3652,7 +3651,7 @@ static int nf_tables_abort(struct sk_buff *skb) { struct net *net = sock_net(skb->sk); struct nft_trans *trans, *next; - struct nft_set *set; + struct nft_trans_elem *te; list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { switch (trans->msg_type) { @@ -3713,9 +3712,13 @@ static int nf_tables_abort(struct sk_buff *skb) break; case NFT_MSG_NEWSETELEM: nft_trans_elem_set(trans)->nelems--; - set = nft_trans_elem_set(trans); - set->ops->get(set, &nft_trans_elem(trans)); - set->ops->remove(set, &nft_trans_elem(trans)); + te = (struct nft_trans_elem *)trans->data; + te->set->ops->get(te->set, &te->elem); + nft_data_uninit(&te->elem.key, NFT_DATA_VALUE); + if (te->set->flags & NFT_SET_MAP && + !(te->elem.flags & NFT_SET_ELEM_INTERVAL_END)) + nft_data_uninit(&te->elem.data, te->set->dtype); + te->set->ops->remove(te->set, &te->elem); nft_trans_destroy(trans); break; case NFT_MSG_DELSETELEM: From ec7b9ff223d7bf939381159c0e59a0d1d209b2e4 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Fri, 6 Mar 2015 16:04:21 +0800 Subject: [PATCH 471/788] ARM: imx6sl-evk: set swbst_reg as vbus's parent reg commit 2de9dd0391a74e80922c1bc95a78cedf85bcdc9e upstream. USB vbus 5V is from PMIC SWBST, so set swbst_reg as vbus's parent reg, it fixed a bug that the voltage of vbus is incorrect due to swbst_reg is disabled after boots up. Signed-off-by: Peter Chen Signed-off-by: Shawn Guo Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/imx6sl-evk.dts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/boot/dts/imx6sl-evk.dts b/arch/arm/boot/dts/imx6sl-evk.dts index fda4932faefda2..945887d3fdb35a 100644 --- a/arch/arm/boot/dts/imx6sl-evk.dts +++ b/arch/arm/boot/dts/imx6sl-evk.dts @@ -52,6 +52,7 @@ regulator-max-microvolt = <5000000>; gpio = <&gpio4 0 0>; enable-active-high; + vin-supply = <&swbst_reg>; }; reg_usb_otg2_vbus: regulator@1 { @@ -62,6 +63,7 @@ regulator-max-microvolt = <5000000>; gpio = <&gpio4 2 0>; enable-active-high; + vin-supply = <&swbst_reg>; }; reg_aud3v: regulator@2 { From fe9c9b2a4545e2ec3eea3369dbd8fccfccd40c44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Mon, 2 Mar 2015 17:18:55 +0100 Subject: [PATCH 472/788] b43: fix support for 5 GHz only BCM43228 model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 0ff66cffde47de51c155ebdd2356403276c04cc4 upstream. It was incorrectly detected as 2 GHz device. Signed-off-by: Rafał Miłecki Signed-off-by: Kalle Valo Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/b43/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 47731cb0d8155d..2fa0dbb41ddecf 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -5322,6 +5322,7 @@ static void b43_supported_bands(struct b43_wldev *dev, bool *have_2ghz_phy, case 0x432a: /* BCM4321 */ case 0x432d: /* BCM4322 */ case 0x4352: /* BCM43222 */ + case 0x435a: /* BCM43228 */ case 0x4333: /* BCM4331 */ case 0x43a2: /* BCM4360 */ case 0x43b3: /* BCM4352 */ From 8ba5046af4483289c78cbb5608e20f695b9a9465 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 27 Feb 2015 05:50:41 +0900 Subject: [PATCH 473/788] ARM: EXYNOS: Don't use LDREX and STREX after disabling cache coherency commit ca489c58ef0b81cc9c9252fd92e6c9bb38d3c408 upstream. During CPU shutdown the exynos_cpu_power_down() is called after disabling cache coherency and it uses LDREX and STREX instructions (by calling of_machine_is_compatible() -> kobject_get() -> kref_get()). The LDREX and STREX should not be used after disabling the cache coherency so just use soc_is_exynos(). Fixes: adc548d77c22 ("ARM: EXYNOS: Use MCPM call-backs to support S2R on exynos5420") Reported-by: Stephen Boyd Signed-off-by: Krzysztof Kozlowski Reviewed-by: Stephen Boyd Signed-off-by: Kukjin Kim Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-exynos/platsmp.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/arm/mach-exynos/platsmp.c b/arch/arm/mach-exynos/platsmp.c index 7a1ebfeeeeb8cf..0abb7dff73ab8f 100644 --- a/arch/arm/mach-exynos/platsmp.c +++ b/arch/arm/mach-exynos/platsmp.c @@ -126,8 +126,7 @@ static inline void platform_do_lowpower(unsigned int cpu, int *spurious) */ void exynos_cpu_power_down(int cpu) { - if (cpu == 0 && (of_machine_is_compatible("samsung,exynos5420") || - of_machine_is_compatible("samsung,exynos5800"))) { + if (cpu == 0 && (soc_is_exynos5420() || soc_is_exynos5800())) { /* * Bypass power down for CPU0 during suspend. Check for * the SYS_PWR_REG value to decide if we are suspending From 14991c854d198cca2bd926ab7b1d27ec207e897d Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Fri, 6 Mar 2015 16:04:20 +0800 Subject: [PATCH 474/788] ARM: imx6qdl-sabresd: set swbst_reg as vbus's parent reg commit 40f737791d4dab26bf23a6331609c604142228bd upstream. USB vbus 5V is from PMIC SWBST, so set swbst_reg as vbus's parent reg, it fixed a bug that the voltage of vbus is incorrect due to swbst_reg is disabled after boots up. Signed-off-by: Peter Chen Signed-off-by: Shawn Guo Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/imx6qdl-sabresd.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/boot/dts/imx6qdl-sabresd.dtsi b/arch/arm/boot/dts/imx6qdl-sabresd.dtsi index f1cd2147421d2e..a626e6dd8022c0 100644 --- a/arch/arm/boot/dts/imx6qdl-sabresd.dtsi +++ b/arch/arm/boot/dts/imx6qdl-sabresd.dtsi @@ -35,6 +35,7 @@ regulator-max-microvolt = <5000000>; gpio = <&gpio3 22 0>; enable-active-high; + vin-supply = <&swbst_reg>; }; reg_usb_h1_vbus: regulator@1 { @@ -45,6 +46,7 @@ regulator-max-microvolt = <5000000>; gpio = <&gpio1 29 0>; enable-active-high; + vin-supply = <&swbst_reg>; }; reg_audio: regulator@2 { From ce0edb9acc328fd99aed43c44ef9fdee5249a9c6 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Tue, 3 Mar 2015 19:58:22 +0100 Subject: [PATCH 475/788] ARM: at91: pm: fix at91rm9200 standby commit 84e871660bebfddb9a62ebd6f19d02536e782f0a upstream. at91rm9200 standby and suspend to ram has been broken since 00482a4078f4. It is wrongly using AT91_BASE_SYS which is a physical address and actually doesn't correspond to any register on at91rm9200. Use the correct at91_ramc_base[0] instead. Fixes: 00482a4078f4 (ARM: at91: implement the standby function for pm/cpuidle) Signed-off-by: Alexandre Belloni Signed-off-by: Nicolas Ferre Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-at91/pm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-at91/pm.h b/arch/arm/mach-at91/pm.h index d2c89963af2d16..86c0aa819d2590 100644 --- a/arch/arm/mach-at91/pm.h +++ b/arch/arm/mach-at91/pm.h @@ -44,7 +44,7 @@ static inline void at91rm9200_standby(void) " mcr p15, 0, %0, c7, c0, 4\n\t" " str %5, [%1, %2]" : - : "r" (0), "r" (AT91_BASE_SYS), "r" (AT91RM9200_SDRAMC_LPR), + : "r" (0), "r" (at91_ramc_base[0]), "r" (AT91RM9200_SDRAMC_LPR), "r" (1), "r" (AT91RM9200_SDRAMC_SRR), "r" (lpr)); } From 0ad91e9c39cf0efe311e4116e9423859d7eea474 Mon Sep 17 00:00:00 2001 From: Ravikumar Kattekola Date: Sat, 31 Jan 2015 22:36:44 +0530 Subject: [PATCH 476/788] ARM: dts: DRA7x: Fix the bypass clock source for dpll_iva and others commit d2192ea09858a8535b056fcede1a41d824e0b3d8 upstream. Fixes: ee6c750761 (ARM: dts: dra7 clock data) On DRA7x, For DPLL_IVA, the ref clock(CLKINP) is connected to sys_clk1 and the bypass input(CLKINPULOW) is connected to iva_dpll_hs_clk_div clock. But the bypass input is not directly routed to bypass clkout instead both CLKINP and CLKINPULOW are connected to bypass clkout via a mux. This mux is controlled by the bit - CM_CLKSEL_DPLL_IVA[23]:DPLL_BYP_CLKSEL and it's POR value is zero which selects the CLKINP as bypass clkout. which means iva_dpll_hs_clk_div is not the bypass clock for dpll_iva_ck Fix this by adding another mux clock as parent in bypass mode. This design is common to most of the PLLs and the rest have only one bypass clock. Below is a list of the DPLLs that need this fix: DPLL_IVA, DPLL_DDR, DPLL_DSP, DPLL_EVE, DPLL_GMAC, DPLL_PER, DPLL_USB and DPLL_CORE Signed-off-by: Ravikumar Kattekola Acked-by: Tero Kristo Signed-off-by: Tony Lindgren Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/dra7xx-clocks.dtsi | 90 +++++++++++++++++++++++++--- 1 file changed, 81 insertions(+), 9 deletions(-) diff --git a/arch/arm/boot/dts/dra7xx-clocks.dtsi b/arch/arm/boot/dts/dra7xx-clocks.dtsi index 4bdcbd61ce47ea..99b09a44e26941 100644 --- a/arch/arm/boot/dts/dra7xx-clocks.dtsi +++ b/arch/arm/boot/dts/dra7xx-clocks.dtsi @@ -243,10 +243,18 @@ ti,invert-autoidle-bit; }; + dpll_core_byp_mux: dpll_core_byp_mux { + #clock-cells = <0>; + compatible = "ti,mux-clock"; + clocks = <&sys_clkin1>, <&dpll_abe_m3x2_ck>; + ti,bit-shift = <23>; + reg = <0x012c>; + }; + dpll_core_ck: dpll_core_ck { #clock-cells = <0>; compatible = "ti,omap4-dpll-core-clock"; - clocks = <&sys_clkin1>, <&dpll_abe_m3x2_ck>; + clocks = <&sys_clkin1>, <&dpll_core_byp_mux>; reg = <0x0120>, <0x0124>, <0x012c>, <0x0128>; }; @@ -309,10 +317,18 @@ clock-div = <1>; }; + dpll_dsp_byp_mux: dpll_dsp_byp_mux { + #clock-cells = <0>; + compatible = "ti,mux-clock"; + clocks = <&sys_clkin1>, <&dsp_dpll_hs_clk_div>; + ti,bit-shift = <23>; + reg = <0x0240>; + }; + dpll_dsp_ck: dpll_dsp_ck { #clock-cells = <0>; compatible = "ti,omap4-dpll-clock"; - clocks = <&sys_clkin1>, <&dsp_dpll_hs_clk_div>; + clocks = <&sys_clkin1>, <&dpll_dsp_byp_mux>; reg = <0x0234>, <0x0238>, <0x0240>, <0x023c>; }; @@ -335,10 +351,18 @@ clock-div = <1>; }; + dpll_iva_byp_mux: dpll_iva_byp_mux { + #clock-cells = <0>; + compatible = "ti,mux-clock"; + clocks = <&sys_clkin1>, <&iva_dpll_hs_clk_div>; + ti,bit-shift = <23>; + reg = <0x01ac>; + }; + dpll_iva_ck: dpll_iva_ck { #clock-cells = <0>; compatible = "ti,omap4-dpll-clock"; - clocks = <&sys_clkin1>, <&iva_dpll_hs_clk_div>; + clocks = <&sys_clkin1>, <&dpll_iva_byp_mux>; reg = <0x01a0>, <0x01a4>, <0x01ac>, <0x01a8>; }; @@ -361,10 +385,18 @@ clock-div = <1>; }; + dpll_gpu_byp_mux: dpll_gpu_byp_mux { + #clock-cells = <0>; + compatible = "ti,mux-clock"; + clocks = <&sys_clkin1>, <&dpll_abe_m3x2_ck>; + ti,bit-shift = <23>; + reg = <0x02e4>; + }; + dpll_gpu_ck: dpll_gpu_ck { #clock-cells = <0>; compatible = "ti,omap4-dpll-clock"; - clocks = <&sys_clkin1>, <&dpll_abe_m3x2_ck>; + clocks = <&sys_clkin1>, <&dpll_gpu_byp_mux>; reg = <0x02d8>, <0x02dc>, <0x02e4>, <0x02e0>; }; @@ -398,10 +430,18 @@ clock-div = <1>; }; + dpll_ddr_byp_mux: dpll_ddr_byp_mux { + #clock-cells = <0>; + compatible = "ti,mux-clock"; + clocks = <&sys_clkin1>, <&dpll_abe_m3x2_ck>; + ti,bit-shift = <23>; + reg = <0x021c>; + }; + dpll_ddr_ck: dpll_ddr_ck { #clock-cells = <0>; compatible = "ti,omap4-dpll-clock"; - clocks = <&sys_clkin1>, <&dpll_abe_m3x2_ck>; + clocks = <&sys_clkin1>, <&dpll_ddr_byp_mux>; reg = <0x0210>, <0x0214>, <0x021c>, <0x0218>; }; @@ -416,10 +456,18 @@ ti,invert-autoidle-bit; }; + dpll_gmac_byp_mux: dpll_gmac_byp_mux { + #clock-cells = <0>; + compatible = "ti,mux-clock"; + clocks = <&sys_clkin1>, <&dpll_abe_m3x2_ck>; + ti,bit-shift = <23>; + reg = <0x02b4>; + }; + dpll_gmac_ck: dpll_gmac_ck { #clock-cells = <0>; compatible = "ti,omap4-dpll-clock"; - clocks = <&sys_clkin1>, <&dpll_abe_m3x2_ck>; + clocks = <&sys_clkin1>, <&dpll_gmac_byp_mux>; reg = <0x02a8>, <0x02ac>, <0x02b4>, <0x02b0>; }; @@ -482,10 +530,18 @@ clock-div = <1>; }; + dpll_eve_byp_mux: dpll_eve_byp_mux { + #clock-cells = <0>; + compatible = "ti,mux-clock"; + clocks = <&sys_clkin1>, <&eve_dpll_hs_clk_div>; + ti,bit-shift = <23>; + reg = <0x0290>; + }; + dpll_eve_ck: dpll_eve_ck { #clock-cells = <0>; compatible = "ti,omap4-dpll-clock"; - clocks = <&sys_clkin1>, <&eve_dpll_hs_clk_div>; + clocks = <&sys_clkin1>, <&dpll_eve_byp_mux>; reg = <0x0284>, <0x0288>, <0x0290>, <0x028c>; }; @@ -1249,10 +1305,18 @@ clock-div = <1>; }; + dpll_per_byp_mux: dpll_per_byp_mux { + #clock-cells = <0>; + compatible = "ti,mux-clock"; + clocks = <&sys_clkin1>, <&per_dpll_hs_clk_div>; + ti,bit-shift = <23>; + reg = <0x014c>; + }; + dpll_per_ck: dpll_per_ck { #clock-cells = <0>; compatible = "ti,omap4-dpll-clock"; - clocks = <&sys_clkin1>, <&per_dpll_hs_clk_div>; + clocks = <&sys_clkin1>, <&dpll_per_byp_mux>; reg = <0x0140>, <0x0144>, <0x014c>, <0x0148>; }; @@ -1275,10 +1339,18 @@ clock-div = <1>; }; + dpll_usb_byp_mux: dpll_usb_byp_mux { + #clock-cells = <0>; + compatible = "ti,mux-clock"; + clocks = <&sys_clkin1>, <&usb_dpll_hs_clk_div>; + ti,bit-shift = <23>; + reg = <0x018c>; + }; + dpll_usb_ck: dpll_usb_ck { #clock-cells = <0>; compatible = "ti,omap4-dpll-j-type-clock"; - clocks = <&sys_clkin1>, <&usb_dpll_hs_clk_div>; + clocks = <&sys_clkin1>, <&dpll_usb_byp_mux>; reg = <0x0180>, <0x0184>, <0x018c>, <0x0188>; }; From 2c2ff0a2dded6f9d60f4addc6ab7a4cf126ad27a Mon Sep 17 00:00:00 2001 From: Vignesh R Date: Tue, 10 Feb 2015 11:05:41 +0530 Subject: [PATCH 477/788] ARM: dts: am33xx-clocks: Fix ehrpwm tbclk data on am33xx commit 6e22616eba7e25fac5aa6cb6563471afa1815ec2 upstream. ehrpwm tbclk is wrongly modelled as deriving from dpll_per_m2_ck. The TRM says tbclk is derived from SYSCLKOUT. SYSCLKOUT nothing but the functional clock of pwmss (l4ls_gclk). Fix this by changing source of ehrpwmx_tbclk to l4ls_gclk. Fixes: 9e100ebafb91: ("Fix ehrpwm tbclk data") Signed-off-by: Vignesh R Acked-by: Tero Kristo Signed-off-by: Tony Lindgren Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/am33xx-clocks.dtsi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/boot/dts/am33xx-clocks.dtsi b/arch/arm/boot/dts/am33xx-clocks.dtsi index 712edce7d6fb12..071b56aa0c7e05 100644 --- a/arch/arm/boot/dts/am33xx-clocks.dtsi +++ b/arch/arm/boot/dts/am33xx-clocks.dtsi @@ -99,7 +99,7 @@ ehrpwm0_tbclk: ehrpwm0_tbclk@44e10664 { #clock-cells = <0>; compatible = "ti,gate-clock"; - clocks = <&dpll_per_m2_ck>; + clocks = <&l4ls_gclk>; ti,bit-shift = <0>; reg = <0x0664>; }; @@ -107,7 +107,7 @@ ehrpwm1_tbclk: ehrpwm1_tbclk@44e10664 { #clock-cells = <0>; compatible = "ti,gate-clock"; - clocks = <&dpll_per_m2_ck>; + clocks = <&l4ls_gclk>; ti,bit-shift = <1>; reg = <0x0664>; }; @@ -115,7 +115,7 @@ ehrpwm2_tbclk: ehrpwm2_tbclk@44e10664 { #clock-cells = <0>; compatible = "ti,gate-clock"; - clocks = <&dpll_per_m2_ck>; + clocks = <&l4ls_gclk>; ti,bit-shift = <2>; reg = <0x0664>; }; From 1f42d197addbbc9457a593c1385d5a6457a3d1ff Mon Sep 17 00:00:00 2001 From: Vignesh R Date: Tue, 10 Feb 2015 11:05:42 +0530 Subject: [PATCH 478/788] ARM: dts: am43xx-clocks: Fix ehrpwm tbclk data on am43xx commit 7d53d25578486d65bd7cd242bc7816b40e55e62b upstream. ehrpwm tbclk is wrongly modelled as deriving from dpll_per_m2_ck. The TRM says tbclk is derived from SYSCLKOUT. SYSCLKOUT nothing but the functional clock of pwmss (l4ls_gclk). Fix this by changing source of ehrpwmx_tbclk to l4ls_gclk. Fixes: 4da1c67719f61 ("add tbclk data for ehrpwm") Signed-off-by: Vignesh R Acked-by: Tero Kristo Signed-off-by: Tony Lindgren Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/am43xx-clocks.dtsi | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/arm/boot/dts/am43xx-clocks.dtsi b/arch/arm/boot/dts/am43xx-clocks.dtsi index c7dc9dab93a45e..cfb49686ab6af0 100644 --- a/arch/arm/boot/dts/am43xx-clocks.dtsi +++ b/arch/arm/boot/dts/am43xx-clocks.dtsi @@ -107,7 +107,7 @@ ehrpwm0_tbclk: ehrpwm0_tbclk { #clock-cells = <0>; compatible = "ti,gate-clock"; - clocks = <&dpll_per_m2_ck>; + clocks = <&l4ls_gclk>; ti,bit-shift = <0>; reg = <0x0664>; }; @@ -115,7 +115,7 @@ ehrpwm1_tbclk: ehrpwm1_tbclk { #clock-cells = <0>; compatible = "ti,gate-clock"; - clocks = <&dpll_per_m2_ck>; + clocks = <&l4ls_gclk>; ti,bit-shift = <1>; reg = <0x0664>; }; @@ -123,7 +123,7 @@ ehrpwm2_tbclk: ehrpwm2_tbclk { #clock-cells = <0>; compatible = "ti,gate-clock"; - clocks = <&dpll_per_m2_ck>; + clocks = <&l4ls_gclk>; ti,bit-shift = <2>; reg = <0x0664>; }; @@ -131,7 +131,7 @@ ehrpwm3_tbclk: ehrpwm3_tbclk { #clock-cells = <0>; compatible = "ti,gate-clock"; - clocks = <&dpll_per_m2_ck>; + clocks = <&l4ls_gclk>; ti,bit-shift = <4>; reg = <0x0664>; }; @@ -139,7 +139,7 @@ ehrpwm4_tbclk: ehrpwm4_tbclk { #clock-cells = <0>; compatible = "ti,gate-clock"; - clocks = <&dpll_per_m2_ck>; + clocks = <&l4ls_gclk>; ti,bit-shift = <5>; reg = <0x0664>; }; @@ -147,7 +147,7 @@ ehrpwm5_tbclk: ehrpwm5_tbclk { #clock-cells = <0>; compatible = "ti,gate-clock"; - clocks = <&dpll_per_m2_ck>; + clocks = <&l4ls_gclk>; ti,bit-shift = <6>; reg = <0x0664>; }; From 2ac13fa4571811a463b1aa8f17b77af5ecae577d Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Wed, 18 Feb 2015 15:33:58 +0100 Subject: [PATCH 479/788] target: Fix reference leak in target_get_sess_cmd() error path commit 7544e597343e2166daba3f32e4708533aa53c233 upstream. This patch fixes a se_cmd->cmd_kref leak buf when se_sess->sess_tearing_down is true within target_get_sess_cmd() submission path code. This se_cmd reference leak can occur during active session shutdown when ack_kref=1 is passed by target_submit_cmd_[map_sgls,tmr]() callers. Signed-off-by: Bart Van Assche Signed-off-by: Nicholas Bellinger Signed-off-by: Greg Kroah-Hartman --- drivers/target/target_core_transport.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 0adc0f6502134e..ac3cbabdbdf024 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -2389,6 +2389,10 @@ int target_get_sess_cmd(struct se_session *se_sess, struct se_cmd *se_cmd, list_add_tail(&se_cmd->se_cmd_list, &se_sess->sess_cmd_list); out: spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); + + if (ret && ack_kref) + target_put_sess_cmd(se_sess, se_cmd); + return ret; } EXPORT_SYMBOL(target_get_sess_cmd); From e079178cc8b74c5ce81b3b23c8dd4c59b32ce792 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Thu, 5 Mar 2015 03:28:24 +0000 Subject: [PATCH 480/788] target: Fix virtual LUN=0 target_configure_device failure OOPs commit 5f7da044f8bc1cfb21c962edf34bd5699a76e7ae upstream. This patch fixes a NULL pointer dereference triggered by a late target_configure_device() -> alloc_workqueue() failure that results in target_free_device() being called with DF_CONFIGURED already set, which subsequently OOPses in destroy_workqueue() code. Currently this only happens at modprobe target_core_mod time when core_dev_setup_virtual_lun0() -> target_configure_device() fails, and the explicit target_free_device() gets called. To address this bug originally introduced by commit 0fd97ccf45, go ahead and move DF_CONFIGURED to end of target_configure_device() code to handle this special failure case. Reported-by: Claudio Fleiner Cc: Claudio Fleiner Cc: Christoph Hellwig Signed-off-by: Nicholas Bellinger Signed-off-by: Greg Kroah-Hartman --- drivers/target/target_core_device.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 58f49ff69b1424..54da2a42049ae7 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -1534,8 +1534,6 @@ int target_configure_device(struct se_device *dev) ret = dev->transport->configure_device(dev); if (ret) goto out; - dev->dev_flags |= DF_CONFIGURED; - /* * XXX: there is not much point to have two different values here.. */ @@ -1597,6 +1595,8 @@ int target_configure_device(struct se_device *dev) list_add_tail(&dev->g_dev_node, &g_device_list); mutex_unlock(&g_device_mutex); + dev->dev_flags |= DF_CONFIGURED; + return 0; out_free_alua: From fcb556db57dc2e83ea8e1c7310b13aff045f367a Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Mon, 23 Feb 2015 00:57:51 -0800 Subject: [PATCH 481/788] iscsi-target: Avoid early conn_logout_comp for iser connections commit f068fbc82e7696d67b1bb8189306865bedf368b6 upstream. This patch fixes a iser specific logout bug where early complete() of conn->conn_logout_comp in iscsit_close_connection() was causing isert_wait4logout() to complete too soon, triggering a use after free NULL pointer dereference of iscsi_conn memory. The complete() was originally added for traditional iscsi-target when a ISCSI_LOGOUT_OP failed in iscsi_target_rx_opcode(), but given iser-target does not wait in logout failure, this special case needs to be avoided. Reported-by: Sagi Grimberg Cc: Sagi Grimberg Cc: Slava Shwartsman Signed-off-by: Nicholas Bellinger Signed-off-by: Greg Kroah-Hartman --- drivers/target/iscsi/iscsi_target.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index aebde3289c50de..8d27db47560ca1 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -4221,11 +4221,17 @@ int iscsit_close_connection( pr_debug("Closing iSCSI connection CID %hu on SID:" " %u\n", conn->cid, sess->sid); /* - * Always up conn_logout_comp just in case the RX Thread is sleeping - * and the logout response never got sent because the connection - * failed. + * Always up conn_logout_comp for the traditional TCP case just in case + * the RX Thread in iscsi_target_rx_opcode() is sleeping and the logout + * response never got sent because the connection failed. + * + * However for iser-target, isert_wait4logout() is using conn_logout_comp + * to signal logout response TX interrupt completion. Go ahead and skip + * this for iser since isert_rx_opcode() does not wait on logout failure, + * and to avoid iscsi_conn pointer dereference in iser-target code. */ - complete(&conn->conn_logout_comp); + if (conn->conn_transport->transport_type == ISCSI_TCP) + complete(&conn->conn_logout_comp); iscsi_release_thread_set(conn); From 6d6efc403b0b939d3175ad2621b16f67d59ad0ed Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Fri, 27 Feb 2015 03:54:13 -0800 Subject: [PATCH 482/788] target/pscsi: Fix NULL pointer dereference in get_device_type commit 215a8fe4198f607f34ecdbc9969dae783d8b5a61 upstream. This patch fixes a NULL pointer dereference OOPs with pSCSI backends within target_core_stat.c code. The bug is caused by a configfs attr read if no pscsi_dev_virt->pdv_sd has been configured. Reported-by: Olaf Hering Signed-off-by: Nicholas Bellinger Signed-off-by: Greg Kroah-Hartman --- drivers/target/target_core_pscsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c index 1045dcd7bf651b..f6c954c4635f5d 100644 --- a/drivers/target/target_core_pscsi.c +++ b/drivers/target/target_core_pscsi.c @@ -1121,7 +1121,7 @@ static u32 pscsi_get_device_type(struct se_device *dev) struct pscsi_dev_virt *pdv = PSCSI_DEV(dev); struct scsi_device *sd = pdv->pdv_sd; - return sd->type; + return (sd) ? sd->type : TYPE_NO_LUN; } static sector_t pscsi_get_blocks(struct se_device *dev) From f65a0d9d16b0bad88a01cdfa957ba7a377b8010c Mon Sep 17 00:00:00 2001 From: Dave Gordon Date: Fri, 6 Mar 2015 15:34:26 +0000 Subject: [PATCH 483/788] drm/i915: use in_interrupt() not in_irq() to check context commit 6c51d46f135b00c00373fcd029786ccef2b02b5b upstream. The kernel in_irq() function tests for hard-IRQ context only, so if a system is run with the kernel 'threadirqs' option selected, the test in intel_check_page_flip() generates lots of warnings, because then it gets called in soft-IRQ context. We can instead use in_interrupt() which allows for either type of interrupt, while still detecting and complaining about misuse of the page flip code if it is ever called from non-interrupt context. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=89321 Signed-off-by: Dave Gordon Reviewed-by: Daniel Vetter Signed-off-by: Jani Nikula Cc: Josh Boyer Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/intel_display.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 30d4eb300be07f..c10b52ef116dc4 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -9702,7 +9702,7 @@ void intel_check_page_flip(struct drm_device *dev, int pipe) struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe]; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - WARN_ON(!in_irq()); + WARN_ON(!in_interrupt()); if (crtc == NULL) return; From d8cf08a565defc2f9810b9ecd33b1b3211b029e1 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 26 Mar 2015 14:00:21 +0100 Subject: [PATCH 484/788] Linux 3.19.3 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e49665a2b5ac2e..713bf263952fe9 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 19 -SUBLEVEL = 2 +SUBLEVEL = 3 EXTRAVERSION = NAME = Diseased Newt From af30befa90cf63b7aa50bdc3bf2a69b8cb27822d Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 27 Feb 2015 14:13:03 +0100 Subject: [PATCH 485/788] ASoC: da732x: Fix control-less DAPM routes commit 8e6a75c102f8e232b599a06e06731d8c5d5f2c5d upstream. Routes without a control must use NULL for the control name. The da732x driver uses "NULL" instead in a few places. Previous to commit 5fe5b767dc6f ("ASoC: dapm: Do not pretend to support controls for non mixer/mux widgets") the DAPM core silently ignored non-NULL controls on non-mixer and non-mux routes. But starting with that commit it will complain and not add the route breaking the da732x driver in the process. This patch replaces the incorrect "NULL" control name with NULL to fix the issue. Fixes: 5fe5b767dc6f ("ASoC: dapm: Do not pretend to support controls for non mixer/mux widgets") Signed-off-by: Lars-Peter Clausen Acked-by: Adam Thomson Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/da732x.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/da732x.c b/sound/soc/codecs/da732x.c index 61b2f9a2eef1b8..e3110c67f3b284 100644 --- a/sound/soc/codecs/da732x.c +++ b/sound/soc/codecs/da732x.c @@ -876,11 +876,11 @@ static const struct snd_soc_dapm_widget da732x_dapm_widgets[] = { static const struct snd_soc_dapm_route da732x_dapm_routes[] = { /* Inputs */ - {"AUX1L PGA", "NULL", "AUX1L"}, - {"AUX1R PGA", "NULL", "AUX1R"}, + {"AUX1L PGA", NULL, "AUX1L"}, + {"AUX1R PGA", NULL, "AUX1R"}, {"MIC1 PGA", NULL, "MIC1"}, - {"MIC2 PGA", "NULL", "MIC2"}, - {"MIC3 PGA", "NULL", "MIC3"}, + {"MIC2 PGA", NULL, "MIC2"}, + {"MIC3 PGA", NULL, "MIC3"}, /* Capture Path */ {"ADC1 Left MUX", "MIC1", "MIC1 PGA"}, From b5a7466c0707e8379a92d4c75376778dd5d3370b Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 27 Feb 2015 14:13:02 +0100 Subject: [PATCH 486/788] ASoC: ak4671: Fix control-less DAPM routes commit ce9594c6b332fd6fe464e22a83b0e6e0a287aac6 upstream. Routes without a control must use NULL for the control name. The ak4671 driver uses "NULL" instead in a few places. Previous to commit 5fe5b767dc6f ("ASoC: dapm: Do not pretend to support controls for non mixer/mux widgets") the DAPM core silently ignored non-NULL controls on non-mixer and non-mux routes. But starting with that commit it will complain and not add the route breaking the ak4671 driver in the process. This patch replaces the incorrect "NULL" control name with NULL to fix the issue. Fixes: 5fe5b767dc6f ("ASoC: dapm: Do not pretend to support controls for non mixer/mux widgets") Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/ak4671.c | 44 +++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c index 686cacb0e835aa..a981c1e8a2cf26 100644 --- a/sound/soc/codecs/ak4671.c +++ b/sound/soc/codecs/ak4671.c @@ -343,25 +343,25 @@ static const struct snd_soc_dapm_widget ak4671_dapm_widgets[] = { }; static const struct snd_soc_dapm_route ak4671_intercon[] = { - {"DAC Left", "NULL", "PMPLL"}, - {"DAC Right", "NULL", "PMPLL"}, - {"ADC Left", "NULL", "PMPLL"}, - {"ADC Right", "NULL", "PMPLL"}, + {"DAC Left", NULL, "PMPLL"}, + {"DAC Right", NULL, "PMPLL"}, + {"ADC Left", NULL, "PMPLL"}, + {"ADC Right", NULL, "PMPLL"}, /* Outputs */ - {"LOUT1", "NULL", "LOUT1 Mixer"}, - {"ROUT1", "NULL", "ROUT1 Mixer"}, - {"LOUT2", "NULL", "LOUT2 Mix Amp"}, - {"ROUT2", "NULL", "ROUT2 Mix Amp"}, - {"LOUT3", "NULL", "LOUT3 Mixer"}, - {"ROUT3", "NULL", "ROUT3 Mixer"}, + {"LOUT1", NULL, "LOUT1 Mixer"}, + {"ROUT1", NULL, "ROUT1 Mixer"}, + {"LOUT2", NULL, "LOUT2 Mix Amp"}, + {"ROUT2", NULL, "ROUT2 Mix Amp"}, + {"LOUT3", NULL, "LOUT3 Mixer"}, + {"ROUT3", NULL, "ROUT3 Mixer"}, {"LOUT1 Mixer", "DACL", "DAC Left"}, {"ROUT1 Mixer", "DACR", "DAC Right"}, {"LOUT2 Mixer", "DACHL", "DAC Left"}, {"ROUT2 Mixer", "DACHR", "DAC Right"}, - {"LOUT2 Mix Amp", "NULL", "LOUT2 Mixer"}, - {"ROUT2 Mix Amp", "NULL", "ROUT2 Mixer"}, + {"LOUT2 Mix Amp", NULL, "LOUT2 Mixer"}, + {"ROUT2 Mix Amp", NULL, "ROUT2 Mixer"}, {"LOUT3 Mixer", "DACSL", "DAC Left"}, {"ROUT3 Mixer", "DACSR", "DAC Right"}, @@ -381,18 +381,18 @@ static const struct snd_soc_dapm_route ak4671_intercon[] = { {"LIN2", NULL, "Mic Bias"}, {"RIN2", NULL, "Mic Bias"}, - {"ADC Left", "NULL", "LIN MUX"}, - {"ADC Right", "NULL", "RIN MUX"}, + {"ADC Left", NULL, "LIN MUX"}, + {"ADC Right", NULL, "RIN MUX"}, /* Analog Loops */ - {"LIN1 Mixing Circuit", "NULL", "LIN1"}, - {"RIN1 Mixing Circuit", "NULL", "RIN1"}, - {"LIN2 Mixing Circuit", "NULL", "LIN2"}, - {"RIN2 Mixing Circuit", "NULL", "RIN2"}, - {"LIN3 Mixing Circuit", "NULL", "LIN3"}, - {"RIN3 Mixing Circuit", "NULL", "RIN3"}, - {"LIN4 Mixing Circuit", "NULL", "LIN4"}, - {"RIN4 Mixing Circuit", "NULL", "RIN4"}, + {"LIN1 Mixing Circuit", NULL, "LIN1"}, + {"RIN1 Mixing Circuit", NULL, "RIN1"}, + {"LIN2 Mixing Circuit", NULL, "LIN2"}, + {"RIN2 Mixing Circuit", NULL, "RIN2"}, + {"LIN3 Mixing Circuit", NULL, "LIN3"}, + {"RIN3 Mixing Circuit", NULL, "RIN3"}, + {"LIN4 Mixing Circuit", NULL, "LIN4"}, + {"RIN4 Mixing Circuit", NULL, "RIN4"}, {"LOUT1 Mixer", "LINL1", "LIN1 Mixing Circuit"}, {"ROUT1 Mixer", "RINR1", "RIN1 Mixing Circuit"}, From 2d08d187dfce83ea2010d03a75f370fa29e7cd7e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 27 Feb 2015 14:13:04 +0100 Subject: [PATCH 487/788] ASoC: sn95031: Fix control-less DAPM routes commit cdd3d2a93f08823a0b9802147dc28c99029dfdfd upstream. Routes without a control must use NULL for the control name. The sn95031 driver uses "NULL" instead in a few places. Previous to commit 5fe5b767dc6f ("ASoC: dapm: Do not pretend to support controls for non mixer/mux widgets") the DAPM core silently ignored non-NULL controls on non-mixer and non-mux routes. But starting with that commit it will complain and not add the route breaking the sn95031 driver in the process. This patch replaces the incorrect "NULL" control name with NULL to fix the issue. Fixes: 5fe5b767dc6f ("ASoC: dapm: Do not pretend to support controls for non mixer/mux widgets") Signed-off-by: Lars-Peter Clausen Acked-by: Vinod Koul Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/sn95031.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c index 1f451a1946eb25..4ff4aa73473b07 100644 --- a/sound/soc/codecs/sn95031.c +++ b/sound/soc/codecs/sn95031.c @@ -531,8 +531,8 @@ static const struct snd_soc_dapm_route sn95031_audio_map[] = { /* speaker map */ { "IHFOUTL", NULL, "Speaker Rail"}, { "IHFOUTR", NULL, "Speaker Rail"}, - { "IHFOUTL", "NULL", "Speaker Left Playback"}, - { "IHFOUTR", "NULL", "Speaker Right Playback"}, + { "IHFOUTL", NULL, "Speaker Left Playback"}, + { "IHFOUTR", NULL, "Speaker Right Playback"}, { "Speaker Left Playback", NULL, "Speaker Left Filter"}, { "Speaker Right Playback", NULL, "Speaker Right Filter"}, { "Speaker Left Filter", NULL, "IHFDAC Left"}, From 165acf5d3e8d84d90347d4f8410a10f6b507b6da Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Fri, 27 Feb 2015 08:06:45 -0700 Subject: [PATCH 488/788] ASoC: sgtl5000: remove useless register write clearing CHRGPUMP_POWERUP commit c7d910b87d3c8e9fcf4077089ca4327c12eee099 upstream. The SGTL5000_CHIP_ANA_POWER register is cached. Update the cached value instead of writing it directly. Patch inspired by Russell King's more colorful remarks in this patch: https://github.com/SolidRun/linux-imx6-3.14/commit/dd4bf6a Signed-off-by: Eric Nelson Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/sgtl5000.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index aa98be32bb60cf..10d2415047ad89 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -1149,13 +1149,7 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec) /* Enable VDDC charge pump */ ana_pwr |= SGTL5000_VDDC_CHRGPMP_POWERUP; } else if (vddio >= 3100 && vdda >= 3100) { - /* - * if vddio and vddd > 3.1v, - * charge pump should be clean before set ana_pwr - */ - snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, - SGTL5000_VDDC_CHRGPMP_POWERUP, 0); - + ana_pwr &= ~SGTL5000_VDDC_CHRGPMP_POWERUP; /* VDDC use VDDIO rail */ lreg_ctrl |= SGTL5000_VDDC_ASSN_OVRD; lreg_ctrl |= SGTL5000_VDDC_MAN_ASSN_VDDIO << From 4f7da4ad6d5873545ffa7bd128ec7e0cc791f78d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 10 Mar 2015 12:39:07 +0100 Subject: [PATCH 489/788] ASoC: pcm1681: Fix wrong value references for boolean kctl commit d7f58db49d9ad92bdb12d21fdc2308b76bc2ed38 upstream. The correct values referred by a boolean control are value.integer.value[], not value.enumerated.item[]. The former is long while the latter is int, so it's even incompatible on 64bit architectures. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/pcm1681.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c index a722a023c26280..477e13d309713e 100644 --- a/sound/soc/codecs/pcm1681.c +++ b/sound/soc/codecs/pcm1681.c @@ -118,7 +118,7 @@ static int pcm1681_get_deemph(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec); - ucontrol->value.enumerated.item[0] = priv->deemph; + ucontrol->value.integer.value[0] = priv->deemph; return 0; } @@ -129,7 +129,7 @@ static int pcm1681_put_deemph(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec); - priv->deemph = ucontrol->value.enumerated.item[0]; + priv->deemph = ucontrol->value.integer.value[0]; return pcm1681_set_deemph(codec); } From 9260766a28a1dbd4edac5e47d231383b249640a2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 10 Mar 2015 12:39:05 +0100 Subject: [PATCH 490/788] ASoC: cs4271: Fix wrong value references for boolean kctl commit e8371aa0fecb73fb8a4b2e0296b025b11e7d6229 upstream. The correct values referred by a boolean control are value.integer.value[], not value.enumerated.item[]. The former is long while the latter is int, so it's even incompatible on 64bit architectures. Signed-off-by: Takashi Iwai Acked-by: Paul Handrigan Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/cs4271.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index 79a4efcb894c19..7d3a6accaf9a4a 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -286,7 +286,7 @@ static int cs4271_get_deemph(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); - ucontrol->value.enumerated.item[0] = cs4271->deemph; + ucontrol->value.integer.value[0] = cs4271->deemph; return 0; } @@ -296,7 +296,7 @@ static int cs4271_put_deemph(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); - cs4271->deemph = ucontrol->value.enumerated.item[0]; + cs4271->deemph = ucontrol->value.integer.value[0]; return cs4271_set_deemph(codec); } From 186e6aca0b7232b0732fa0ed84ac9317bdb46588 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 10 Mar 2015 12:39:06 +0100 Subject: [PATCH 491/788] ASoC: es8238: Fix wrong value references for boolean kctl commit d223b0e7fcfecc23380e7de45eb6a0e7b328c17c upstream. The correct values referred by a boolean control are value.integer.value[], not value.enumerated.item[]. The former is long while the latter is int, so it's even incompatible on 64bit architectures. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/es8328.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index f27325155acef3..c5f35a07e8e481 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -120,7 +120,7 @@ static int es8328_get_deemph(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec); - ucontrol->value.enumerated.item[0] = es8328->deemph; + ucontrol->value.integer.value[0] = es8328->deemph; return 0; } @@ -129,7 +129,7 @@ static int es8328_put_deemph(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec); - int deemph = ucontrol->value.enumerated.item[0]; + int deemph = ucontrol->value.integer.value[0]; int ret; if (deemph > 1) From 063419e9871a127061b62e1d4e8f7e6958c7ce9d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 10 Mar 2015 12:39:14 +0100 Subject: [PATCH 492/788] ASoC: wm8960: Fix wrong value references for boolean kctl commit b4a18c8b1af15ebfa9054a3d2aef7b0a7e6f2a05 upstream. The correct values referred by a boolean control are value.integer.value[], not value.enumerated.item[]. The former is long while the latter is int, so it's even incompatible on 64bit architectures. Signed-off-by: Takashi Iwai Acked-by: Charles Keepax Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/wm8960.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index a96eb497a3796e..0435aeba211081 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -182,7 +182,7 @@ static int wm8960_get_deemph(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); - ucontrol->value.enumerated.item[0] = wm8960->deemph; + ucontrol->value.integer.value[0] = wm8960->deemph; return 0; } @@ -191,7 +191,7 @@ static int wm8960_put_deemph(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); - int deemph = ucontrol->value.enumerated.item[0]; + int deemph = ucontrol->value.integer.value[0]; if (deemph > 1) return -EINVAL; From d33d9446f0432536722714e245b128a1e72a7df4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 10 Mar 2015 12:39:08 +0100 Subject: [PATCH 493/788] ASoC: tas5086: Fix wrong value references for boolean kctl commit 4c523ef61160b7d478371ddc9f48c8ce0a00d675 upstream. The correct values referred by a boolean control are value.integer.value[], not value.enumerated.item[]. The former is long while the latter is int, so it's even incompatible on 64bit architectures. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/tas5086.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c index 249ef5c4c76279..32942bed34b1e9 100644 --- a/sound/soc/codecs/tas5086.c +++ b/sound/soc/codecs/tas5086.c @@ -281,7 +281,7 @@ static int tas5086_get_deemph(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec); - ucontrol->value.enumerated.item[0] = priv->deemph; + ucontrol->value.integer.value[0] = priv->deemph; return 0; } @@ -292,7 +292,7 @@ static int tas5086_put_deemph(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec); - priv->deemph = ucontrol->value.enumerated.item[0]; + priv->deemph = ucontrol->value.integer.value[0]; return tas5086_set_deemph(codec); } From f841ad938f36069b5b755a84b55cc6cb4a0dddf4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 10 Mar 2015 12:39:10 +0100 Subject: [PATCH 494/788] ASoC: wm8731: Fix wrong value references for boolean kctl commit bd14016fbf31aa199026f1e2358eab695f374eb1 upstream. The correct values referred by a boolean control are value.integer.value[], not value.enumerated.item[]. The former is long while the latter is int, so it's even incompatible on 64bit architectures. Signed-off-by: Takashi Iwai Acked-by: Charles Keepax Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/wm8731.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index b115ed815db973..391534f774b186 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -125,7 +125,7 @@ static int wm8731_get_deemph(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); - ucontrol->value.enumerated.item[0] = wm8731->deemph; + ucontrol->value.integer.value[0] = wm8731->deemph; return 0; } @@ -135,7 +135,7 @@ static int wm8731_put_deemph(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); - int deemph = ucontrol->value.enumerated.item[0]; + int deemph = ucontrol->value.integer.value[0]; int ret = 0; if (deemph > 1) From 26e71a043d7412a8d0a38e61e59e00c3942fedcd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 10 Mar 2015 12:39:09 +0100 Subject: [PATCH 495/788] ASoC: wm2000: Fix wrong value references for boolean kctl commit 00a14c2968e3d55817e0fa35c78106ca840537bf upstream. The correct values referred by a boolean control are value.integer.value[], not value.enumerated.item[]. The former is long while the latter is int, so it's even incompatible on 64bit architectures. Signed-off-by: Takashi Iwai Acked-by: Charles Keepax Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/wm2000.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index 34ef65c52a7d94..8eeab47a423595 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -610,7 +610,7 @@ static int wm2000_anc_mode_get(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev); - ucontrol->value.enumerated.item[0] = wm2000->anc_active; + ucontrol->value.integer.value[0] = wm2000->anc_active; return 0; } @@ -620,7 +620,7 @@ static int wm2000_anc_mode_put(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev); - int anc_active = ucontrol->value.enumerated.item[0]; + int anc_active = ucontrol->value.integer.value[0]; int ret; if (anc_active > 1) @@ -643,7 +643,7 @@ static int wm2000_speaker_get(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev); - ucontrol->value.enumerated.item[0] = wm2000->spk_ena; + ucontrol->value.integer.value[0] = wm2000->spk_ena; return 0; } @@ -653,7 +653,7 @@ static int wm2000_speaker_put(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev); - int val = ucontrol->value.enumerated.item[0]; + int val = ucontrol->value.integer.value[0]; int ret; if (val > 1) From 26b85c46b33ff70560cc472ab23b9c40a9f4023a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 10 Mar 2015 12:39:11 +0100 Subject: [PATCH 496/788] ASoC: wm8903: Fix wrong value references for boolean kctl commit 24cc883c1fd16df34211ae41624aa6d3cd906693 upstream. The correct values referred by a boolean control are value.integer.value[], not value.enumerated.item[]. The former is long while the latter is int, so it's even incompatible on 64bit architectures. Signed-off-by: Takashi Iwai Acked-by: Charles Keepax Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/wm8903.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index cc6b0ef98a345e..15435c995fd0da 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -442,7 +442,7 @@ static int wm8903_get_deemph(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); - ucontrol->value.enumerated.item[0] = wm8903->deemph; + ucontrol->value.integer.value[0] = wm8903->deemph; return 0; } @@ -452,7 +452,7 @@ static int wm8903_put_deemph(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); - int deemph = ucontrol->value.enumerated.item[0]; + int deemph = ucontrol->value.integer.value[0]; int ret = 0; if (deemph > 1) From 1663fd40c9611a5e132a858e2b82c4cf5a128357 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 10 Mar 2015 12:39:12 +0100 Subject: [PATCH 497/788] ASoC: wm8904: Fix wrong value references for boolean kctl commit eaddf6fd959074f6a6e71deffe079c71eef35da6 upstream. The correct values referred by a boolean control are value.integer.value[], not value.enumerated.item[]. The former is long while the latter is int, so it's even incompatible on 64bit architectures. Signed-off-by: Takashi Iwai Acked-by: Charles Keepax Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/wm8904.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 75b87c5c0f0462..b945a3be067f03 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -525,7 +525,7 @@ static int wm8904_get_deemph(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec); - ucontrol->value.enumerated.item[0] = wm8904->deemph; + ucontrol->value.integer.value[0] = wm8904->deemph; return 0; } @@ -534,7 +534,7 @@ static int wm8904_put_deemph(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec); - int deemph = ucontrol->value.enumerated.item[0]; + int deemph = ucontrol->value.integer.value[0]; if (deemph > 1) return -EINVAL; From 3564730f7882b7bc59004047675baf2daece3a35 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 10 Mar 2015 12:39:04 +0100 Subject: [PATCH 498/788] ASoC: ak4641: Fix wrong value references for boolean kctl commit 08641d9b7bf915144a57a736b42642e13eb1167f upstream. The correct values referred by a boolean control are value.integer.value[], not value.enumerated.item[]. The former is long while the latter is int, so it's even incompatible on 64bit architectures. Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/ak4641.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c index 70861c7b1631ab..81b54a270bd8f8 100644 --- a/sound/soc/codecs/ak4641.c +++ b/sound/soc/codecs/ak4641.c @@ -76,7 +76,7 @@ static int ak4641_put_deemph(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec); - int deemph = ucontrol->value.enumerated.item[0]; + int deemph = ucontrol->value.integer.value[0]; if (deemph > 1) return -EINVAL; @@ -92,7 +92,7 @@ static int ak4641_get_deemph(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec); - ucontrol->value.enumerated.item[0] = ak4641->deemph; + ucontrol->value.integer.value[0] = ak4641->deemph; return 0; }; From 8c6c177489646e582eb0de5e5cf251f5cb3d1ef8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 10 Mar 2015 12:39:03 +0100 Subject: [PATCH 499/788] ASoC: adav80x: Fix wrong value references for boolean kctl commit 2bf4c1d483d911cda5dd385527194d23e5cea73d upstream. The correct values referred by a boolean control are value.integer.value[], not value.enumerated.item[]. The former is long while the latter is int, so it's even incompatible on 64bit architectures. Signed-off-by: Takashi Iwai Acked-by: Lars-Peter Clausen Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/adav80x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c index b67480f1b1aa4a..4373ada95648e6 100644 --- a/sound/soc/codecs/adav80x.c +++ b/sound/soc/codecs/adav80x.c @@ -317,7 +317,7 @@ static int adav80x_put_deemph(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); - unsigned int deemph = ucontrol->value.enumerated.item[0]; + unsigned int deemph = ucontrol->value.integer.value[0]; if (deemph > 1) return -EINVAL; @@ -333,7 +333,7 @@ static int adav80x_get_deemph(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); - ucontrol->value.enumerated.item[0] = adav80x->deemph; + ucontrol->value.integer.value[0] = adav80x->deemph; return 0; }; From 3427a69e0d91ef21d56451f648a0e9ab356d966d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 10 Mar 2015 12:39:13 +0100 Subject: [PATCH 500/788] ASoC: wm8955: Fix wrong value references for boolean kctl commit 07892b10356f17717abdc578acbef72db86c880e upstream. The correct values referred by a boolean control are value.integer.value[], not value.enumerated.item[]. The former is long while the latter is int, so it's even incompatible on 64bit architectures. Signed-off-by: Takashi Iwai Acked-by: Charles Keepax Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/wm8955.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c index 1173f7fef5a76d..035bdc41c81d92 100644 --- a/sound/soc/codecs/wm8955.c +++ b/sound/soc/codecs/wm8955.c @@ -393,7 +393,7 @@ static int wm8955_get_deemph(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec); - ucontrol->value.enumerated.item[0] = wm8955->deemph; + ucontrol->value.integer.value[0] = wm8955->deemph; return 0; } @@ -402,7 +402,7 @@ static int wm8955_put_deemph(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec); - int deemph = ucontrol->value.enumerated.item[0]; + int deemph = ucontrol->value.integer.value[0]; if (deemph > 1) return -EINVAL; From ecec5e543d85a6aec0e920c5c9dbb6dee6691f45 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 10 Mar 2015 12:39:15 +0100 Subject: [PATCH 501/788] ASoC: wm9712: Fix wrong value references for boolean kctl commit 4b0b669b86a963f71feaa1a694e881832fdf4f86 upstream. The correct values referred by a boolean control are value.integer.value[], not value.enumerated.item[]. The former is long while the latter is int, so it's even incompatible on 64bit architectures. Signed-off-by: Takashi Iwai Acked-by: Charles Keepax Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/wm9712.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index 9517571e820d95..98c9525bd751fb 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -180,7 +180,7 @@ static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec); - unsigned int val = ucontrol->value.enumerated.item[0]; + unsigned int val = ucontrol->value.integer.value[0]; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int mixer, mask, shift, old; @@ -193,7 +193,7 @@ static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol, mutex_lock(&wm9712->lock); old = wm9712->hp_mixer[mixer]; - if (ucontrol->value.enumerated.item[0]) + if (ucontrol->value.integer.value[0]) wm9712->hp_mixer[mixer] |= mask; else wm9712->hp_mixer[mixer] &= ~mask; @@ -231,7 +231,7 @@ static int wm9712_hp_mixer_get(struct snd_kcontrol *kcontrol, mixer = mc->shift >> 8; shift = mc->shift & 0xff; - ucontrol->value.enumerated.item[0] = + ucontrol->value.integer.value[0] = (wm9712->hp_mixer[mixer] >> shift) & 1; return 0; From 5a903e6ae7e6185600a22ecaa3582ef1ffd3cf9c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 10 Mar 2015 12:39:16 +0100 Subject: [PATCH 502/788] ASoC: wm9713: Fix wrong value references for boolean kctl commit 87a8b286e2f63c048a586dc677140d4a5b5808aa upstream. The correct values referred by a boolean control are value.integer.value[], not value.enumerated.item[]. The former is long while the latter is int, so it's even incompatible on 64bit architectures. Signed-off-by: Takashi Iwai Acked-by: Charles Keepax Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/wm9713.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 6ab1122a3872de..db644c66809e02 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -255,7 +255,7 @@ static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); - unsigned int val = ucontrol->value.enumerated.item[0]; + unsigned int val = ucontrol->value.integer.value[0]; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int mixer, mask, shift, old; @@ -268,7 +268,7 @@ static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol, mutex_lock(&wm9713->lock); old = wm9713->hp_mixer[mixer]; - if (ucontrol->value.enumerated.item[0]) + if (ucontrol->value.integer.value[0]) wm9713->hp_mixer[mixer] |= mask; else wm9713->hp_mixer[mixer] &= ~mask; @@ -306,7 +306,7 @@ static int wm9713_hp_mixer_get(struct snd_kcontrol *kcontrol, mixer = mc->shift >> 8; shift = mc->shift & 0xff; - ucontrol->value.enumerated.item[0] = + ucontrol->value.integer.value[0] = (wm9713->hp_mixer[mixer] >> shift) & 1; return 0; From 757fd0d78d090eb2518b09a5903d312e3955c08d Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 5 Mar 2015 13:24:41 +1030 Subject: [PATCH 503/788] virtio_balloon: set DRIVER_OK before using device commit 88660f7fb94cda1f8f63ee92bfcd0db39a6361e2 upstream. virtio spec requires that all drivers set DRIVER_OK before using devices. While balloon isn't yet included in the virtio 1 spec, previous spec versions also required this. virtio balloon might violate this rule: probe calls kthread_run before setting DRIVER_OK, which might run immediately and cause balloon to inflate/deflate. To fix, call virtio_device_ready before running the kthread. Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell Signed-off-by: Greg Kroah-Hartman --- drivers/virtio/virtio_balloon.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 50c5f42d7a9f30..8a969320ad423f 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -494,6 +494,8 @@ static int virtballoon_probe(struct virtio_device *vdev) if (err < 0) goto out_oom_notify; + virtio_device_ready(vdev); + vb->thread = kthread_run(balloon, vb, "vballoon"); if (IS_ERR(vb->thread)) { err = PTR_ERR(vb->thread); From 08356188de276d7c08130dbe146d618fe9b3d76b Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 10 Mar 2015 11:55:08 +1030 Subject: [PATCH 504/788] virtio-balloon: do not call blocking ops when !TASK_RUNNING commit 3d2a3774c1b046f548ebea0391a602fd5685a307 upstream. virtio balloon has this code: wait_event_interruptible(vb->config_change, (diff = towards_target(vb)) != 0 || vb->need_stats_update || kthread_should_stop() || freezing(current)); Which is a problem because towards_target() call might block after wait_event_interruptible sets task state to TAST_INTERRUPTIBLE, causing the task_struct::state collision typical of nesting of sleeping primitives See also http://lwn.net/Articles/628628/ or Thomas's bug report http://article.gmane.org/gmane.linux.kernel.virtualization/24846 for a fuller explanation. To fix, rewrite using wait_woken. Reported-by: Thomas Huth Signed-off-by: Michael S. Tsirkin Tested-by: Thomas Huth Reviewed-by: Cornelia Huck Signed-off-by: Rusty Russell Signed-off-by: Greg Kroah-Hartman --- drivers/virtio/virtio_balloon.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 8a969320ad423f..b8b7a6c712ae90 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -29,6 +29,7 @@ #include #include #include +#include /* * Balloon device works in 4K page units. So each page is pointed to by @@ -335,17 +336,25 @@ static int virtballoon_oom_notify(struct notifier_block *self, static int balloon(void *_vballoon) { struct virtio_balloon *vb = _vballoon; + DEFINE_WAIT_FUNC(wait, woken_wake_function); set_freezable(); while (!kthread_should_stop()) { s64 diff; try_to_freeze(); - wait_event_interruptible(vb->config_change, - (diff = towards_target(vb)) != 0 - || vb->need_stats_update - || kthread_should_stop() - || freezing(current)); + + add_wait_queue(&vb->config_change, &wait); + for (;;) { + if ((diff = towards_target(vb)) != 0 || + vb->need_stats_update || + kthread_should_stop() || + freezing(current)) + break; + wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); + } + remove_wait_queue(&vb->config_change, &wait); + if (vb->need_stats_update) stats_handle_request(vb); if (diff > 0) From 0e1e3b03a3edcc2fd5a56fbbbc03d6eec07146c9 Mon Sep 17 00:00:00 2001 From: Yongbae Park Date: Tue, 3 Mar 2015 13:05:48 +0900 Subject: [PATCH 505/788] clockevents: sun5i: Fix setup_irq init sequence commit 1096be084ac59927158ce80ff1d31c33eed0e565 upstream. The interrupt is enabled before the handler is set. Even this bug did not appear, it is potentially dangerous as it can lead to a NULL pointer dereference. Fix the error by enabling the interrupt after clockevents_config_and_register() is called. Signed-off-by: Yongbae Park Signed-off-by: Daniel Lezcano Signed-off-by: Greg Kroah-Hartman --- drivers/clocksource/timer-sun5i.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/clocksource/timer-sun5i.c b/drivers/clocksource/timer-sun5i.c index 02268448dc8540..5dcbf90b8015ce 100644 --- a/drivers/clocksource/timer-sun5i.c +++ b/drivers/clocksource/timer-sun5i.c @@ -178,10 +178,6 @@ static void __init sun5i_timer_init(struct device_node *node) ticks_per_jiffy = DIV_ROUND_UP(rate, HZ); - ret = setup_irq(irq, &sun5i_timer_irq); - if (ret) - pr_warn("failed to setup irq %d\n", irq); - /* Enable timer0 interrupt */ val = readl(timer_base + TIMER_IRQ_EN_REG); writel(val | TIMER_IRQ_EN(0), timer_base + TIMER_IRQ_EN_REG); @@ -191,6 +187,10 @@ static void __init sun5i_timer_init(struct device_node *node) clockevents_config_and_register(&sun5i_clockevent, rate, TIMER_SYNC_TICKS, 0xffffffff); + + ret = setup_irq(irq, &sun5i_timer_irq); + if (ret) + pr_warn("failed to setup irq %d\n", irq); } CLOCKSOURCE_OF_DECLARE(sun5i_a13, "allwinner,sun5i-a13-hstimer", sun5i_timer_init); From 42785aa288a55a7e2ec5a5c5e4ec87ee9c149336 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 7 Mar 2015 17:10:01 +0100 Subject: [PATCH 506/788] regmap: regcache-rbtree: Fix present bitmap resize commit 328f494d95aac8bd4896aea2328bc281053bcb71 upstream. When inserting a new register into a block at the lower end the present bitmap is currently shifted into the wrong direction. The effect of this is that the bitmap becomes corrupted and registers which are present might be reported as not present and vice versa. Fix this by shifting left rather than right. Fixes: 472fdec7380c("regmap: rbtree: Reduce number of nodes, take 2") Reported-by: Daniel Baluta Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/base/regmap/regcache-rbtree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index d453a2c98ad0a6..81751a49d8bf23 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -307,7 +307,7 @@ static int regcache_rbtree_insert_to_block(struct regmap *map, if (pos == 0) { memmove(blk + offset * map->cache_word_size, blk, rbnode->blklen * map->cache_word_size); - bitmap_shift_right(present, present, offset, blklen); + bitmap_shift_left(present, present, offset, blklen); } /* update the rbnode block, its size and the base register */ From 55a86f9e3ca7fb1f95bd806a6bf91ba2cd74f4a6 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 9 Mar 2015 12:20:13 +0100 Subject: [PATCH 507/788] regmap: introduce regmap_name to fix syscon regmap trace events commit c6b570d97c0e77f570bb6b2ed30d372b2b1e9aae upstream. This patch fixes a NULL pointer dereference when enabling regmap event tracing in the presence of a syscon regmap, introduced by commit bdb0066df96e ("mfd: syscon: Decouple syscon interface from platform devices"). That patch introduced syscon regmaps that have their dev field set to NULL. The regmap trace events expect it to point to a valid struct device and feed it to dev_name(): $ echo 1 > /sys/kernel/debug/tracing/events/regmap/enable Unable to handle kernel NULL pointer dereference at virtual address 0000002c pgd = 80004000 [0000002c] *pgd=00000000 Internal error: Oops: 17 [#1] SMP ARM Modules linked in: coda videobuf2_vmalloc CPU: 0 PID: 304 Comm: kworker/0:2 Not tainted 4.0.0-rc2+ #9197 Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree) Workqueue: events_freezable thermal_zone_device_check task: 9f25a200 ti: 9f1ee000 task.ti: 9f1ee000 PC is at ftrace_raw_event_regmap_block+0x3c/0xe4 LR is at _regmap_raw_read+0x1bc/0x1cc pc : [<803636e8>] lr : [<80365f2c>] psr: 600f0093 sp : 9f1efd78 ip : 9f1efdb8 fp : 9f1efdb4 r10: 00000004 r9 : 00000001 r8 : 00000001 r7 : 00000180 r6 : 00000000 r5 : 9f00e3c0 r4 : 00000003 r3 : 00000001 r2 : 00000180 r1 : 00000000 r0 : 9f00e3c0 Flags: nZCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment kernel Control: 10c5387d Table: 2d91004a DAC: 00000015 Process kworker/0:2 (pid: 304, stack limit = 0x9f1ee210) Stack: (0x9f1efd78 to 0x9f1f0000) fd60: 9f1efda4 9f1efd88 fd80: 800708c0 805f9510 80927140 800f0013 9f1fc800 9eb2f490 00000000 00000180 fda0: 808e3840 00000001 9f1efdfc 9f1efdb8 80365f2c 803636b8 805f8958 800708e0 fdc0: a00f0013 803636ac 9f16de00 00000180 80927140 9f1fc800 9f1fc800 9f1efe6c fde0: 9f1efe6c 9f732400 00000000 00000000 9f1efe1c 9f1efe00 80365f70 80365d7c fe00: 80365f3c 9f1fc800 9f1fc800 00000180 9f1efe44 9f1efe20 803656a4 80365f48 fe20: 9f1fc800 00000180 9f1efe6c 9f1efe6c 9f732400 00000000 9f1efe64 9f1efe48 fe40: 803657bc 80365634 00000001 9e95f910 9f1fc800 9f1efeb4 9f1efe8c 9f1efe68 fe60: 80452ac0 80365778 9f1efe8c 9f1efe78 9e93d400 9e93d5e8 9f1efeb4 9f72ef40 fe80: 9f1efeac 9f1efe90 8044e11c 80452998 8045298c 9e93d608 9e93d400 808e1978 fea0: 9f1efecc 9f1efeb0 8044fd14 8044e0d0 ffffffff 9f25a200 9e93d608 9e481380 fec0: 9f1efedc 9f1efed0 8044fde8 8044fcec 9f1eff1c 9f1efee0 80038d50 8044fdd8 fee0: 9f1ee020 9f72ef40 9e481398 00000000 00000008 9f72ef54 9f1ee020 9f72ef40 ff00: 9e481398 9e481380 00000008 9f72ef40 9f1eff5c 9f1eff20 80039754 80038bfc ff20: 00000000 9e481380 80894100 808e1662 00000000 9e4f2ec0 00000000 9e481380 ff40: 800396f8 00000000 00000000 00000000 9f1effac 9f1eff60 8003e020 80039704 ff60: ffffffff 00000000 ffffffff 9e481380 00000000 00000000 9f1eff78 9f1eff78 ff80: 00000000 00000000 9f1eff88 9f1eff88 9e4f2ec0 8003df30 00000000 00000000 ffa0: 00000000 9f1effb0 8000eb60 8003df3c 00000000 00000000 00000000 00000000 ffc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ffe0: 00000000 00000000 00000000 00000000 00000013 00000000 ffffffff ffffffff Backtrace: [<803636ac>] (ftrace_raw_event_regmap_block) from [<80365f2c>] (_regmap_raw_read+0x1bc/0x1cc) r9:00000001 r8:808e3840 r7:00000180 r6:00000000 r5:9eb2f490 r4:9f1fc800 [<80365d70>] (_regmap_raw_read) from [<80365f70>] (_regmap_bus_read+0x34/0x6c) r10:00000000 r9:00000000 r8:9f732400 r7:9f1efe6c r6:9f1efe6c r5:9f1fc800 r4:9f1fc800 [<80365f3c>] (_regmap_bus_read) from [<803656a4>] (_regmap_read+0x7c/0x144) r6:00000180 r5:9f1fc800 r4:9f1fc800 r3:80365f3c [<80365628>] (_regmap_read) from [<803657bc>] (regmap_read+0x50/0x70) r9:00000000 r8:9f732400 r7:9f1efe6c r6:9f1efe6c r5:00000180 r4:9f1fc800 [<8036576c>] (regmap_read) from [<80452ac0>] (imx_get_temp+0x134/0x1a4) r6:9f1efeb4 r5:9f1fc800 r4:9e95f910 r3:00000001 [<8045298c>] (imx_get_temp) from [<8044e11c>] (thermal_zone_get_temp+0x58/0x74) r7:9f72ef40 r6:9f1efeb4 r5:9e93d5e8 r4:9e93d400 [<8044e0c4>] (thermal_zone_get_temp) from [<8044fd14>] (thermal_zone_device_update+0x34/0xec) r6:808e1978 r5:9e93d400 r4:9e93d608 r3:8045298c [<8044fce0>] (thermal_zone_device_update) from [<8044fde8>] (thermal_zone_device_check+0x1c/0x20) r5:9e481380 r4:9e93d608 [<8044fdcc>] (thermal_zone_device_check) from [<80038d50>] (process_one_work+0x160/0x3d4) [<80038bf0>] (process_one_work) from [<80039754>] (worker_thread+0x5c/0x4f4) r10:9f72ef40 r9:00000008 r8:9e481380 r7:9e481398 r6:9f72ef40 r5:9f1ee020 r4:9f72ef54 [<800396f8>] (worker_thread) from [<8003e020>] (kthread+0xf0/0x108) r10:00000000 r9:00000000 r8:00000000 r7:800396f8 r6:9e481380 r5:00000000 r4:9e4f2ec0 [<8003df30>] (kthread) from [<8000eb60>] (ret_from_fork+0x14/0x34) r7:00000000 r6:00000000 r5:8003df30 r4:9e4f2ec0 Code: e3140040 1a00001a e3140020 1a000016 (e596002c) ---[ end trace 193c15c2494ec960 ]--- Fixes: bdb0066df96e (mfd: syscon: Decouple syscon interface from platform devices) Signed-off-by: Philipp Zabel Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/base/regmap/internal.h | 8 +++ drivers/base/regmap/regcache.c | 16 ++--- drivers/base/regmap/regmap.c | 32 ++++----- include/trace/events/regmap.h | 123 ++++++++++++++++----------------- 4 files changed, 91 insertions(+), 88 deletions(-) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 0da5865df5b1b4..8e1f5f67e25f72 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -237,4 +237,12 @@ extern struct regcache_ops regcache_rbtree_ops; extern struct regcache_ops regcache_lzo_ops; extern struct regcache_ops regcache_flat_ops; +static inline const char *regmap_name(const struct regmap *map) +{ + if (map->dev) + return dev_name(map->dev); + + return map->name; +} + #endif diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index f373c35f9e1db2..f5db662e951e71 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -218,7 +218,7 @@ int regcache_read(struct regmap *map, ret = map->cache_ops->read(map, reg, value); if (ret == 0) - trace_regmap_reg_read_cache(map->dev, reg, *value); + trace_regmap_reg_read_cache(map, reg, *value); return ret; } @@ -311,7 +311,7 @@ int regcache_sync(struct regmap *map) dev_dbg(map->dev, "Syncing %s cache\n", map->cache_ops->name); name = map->cache_ops->name; - trace_regcache_sync(map->dev, name, "start"); + trace_regcache_sync(map, name, "start"); if (!map->cache_dirty) goto out; @@ -346,7 +346,7 @@ int regcache_sync(struct regmap *map) regmap_async_complete(map); - trace_regcache_sync(map->dev, name, "stop"); + trace_regcache_sync(map, name, "stop"); return ret; } @@ -381,7 +381,7 @@ int regcache_sync_region(struct regmap *map, unsigned int min, name = map->cache_ops->name; dev_dbg(map->dev, "Syncing %s cache from %d-%d\n", name, min, max); - trace_regcache_sync(map->dev, name, "start region"); + trace_regcache_sync(map, name, "start region"); if (!map->cache_dirty) goto out; @@ -401,7 +401,7 @@ int regcache_sync_region(struct regmap *map, unsigned int min, regmap_async_complete(map); - trace_regcache_sync(map->dev, name, "stop region"); + trace_regcache_sync(map, name, "stop region"); return ret; } @@ -428,7 +428,7 @@ int regcache_drop_region(struct regmap *map, unsigned int min, map->lock(map->lock_arg); - trace_regcache_drop_region(map->dev, min, max); + trace_regcache_drop_region(map, min, max); ret = map->cache_ops->drop(map, min, max); @@ -455,7 +455,7 @@ void regcache_cache_only(struct regmap *map, bool enable) map->lock(map->lock_arg); WARN_ON(map->cache_bypass && enable); map->cache_only = enable; - trace_regmap_cache_only(map->dev, enable); + trace_regmap_cache_only(map, enable); map->unlock(map->lock_arg); } EXPORT_SYMBOL_GPL(regcache_cache_only); @@ -493,7 +493,7 @@ void regcache_cache_bypass(struct regmap *map, bool enable) map->lock(map->lock_arg); WARN_ON(map->cache_only && enable); map->cache_bypass = enable; - trace_regmap_cache_bypass(map->dev, enable); + trace_regmap_cache_bypass(map, enable); map->unlock(map->lock_arg); } EXPORT_SYMBOL_GPL(regcache_cache_bypass); diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index d2f8a818d20068..ee731bb7d9572d 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1280,7 +1280,7 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, if (map->async && map->bus->async_write) { struct regmap_async *async; - trace_regmap_async_write_start(map->dev, reg, val_len); + trace_regmap_async_write_start(map, reg, val_len); spin_lock_irqsave(&map->async_lock, flags); async = list_first_entry_or_null(&map->async_free, @@ -1338,8 +1338,7 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, return ret; } - trace_regmap_hw_write_start(map->dev, reg, - val_len / map->format.val_bytes); + trace_regmap_hw_write_start(map, reg, val_len / map->format.val_bytes); /* If we're doing a single register write we can probably just * send the work_buf directly, otherwise try to do a gather @@ -1371,8 +1370,7 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, kfree(buf); } - trace_regmap_hw_write_done(map->dev, reg, - val_len / map->format.val_bytes); + trace_regmap_hw_write_done(map, reg, val_len / map->format.val_bytes); return ret; } @@ -1406,12 +1404,12 @@ static int _regmap_bus_formatted_write(void *context, unsigned int reg, map->format.format_write(map, reg, val); - trace_regmap_hw_write_start(map->dev, reg, 1); + trace_regmap_hw_write_start(map, reg, 1); ret = map->bus->write(map->bus_context, map->work_buf, map->format.buf_size); - trace_regmap_hw_write_done(map->dev, reg, 1); + trace_regmap_hw_write_done(map, reg, 1); return ret; } @@ -1469,7 +1467,7 @@ int _regmap_write(struct regmap *map, unsigned int reg, dev_info(map->dev, "%x <= %x\n", reg, val); #endif - trace_regmap_reg_write(map->dev, reg, val); + trace_regmap_reg_write(map, reg, val); return map->reg_write(context, reg, val); } @@ -1772,7 +1770,7 @@ static int _regmap_raw_multi_reg_write(struct regmap *map, for (i = 0; i < num_regs; i++) { int reg = regs[i].reg; int val = regs[i].def; - trace_regmap_hw_write_start(map->dev, reg, 1); + trace_regmap_hw_write_start(map, reg, 1); map->format.format_reg(u8, reg, map->reg_shift); u8 += reg_bytes + pad_bytes; map->format.format_val(u8, val, 0); @@ -1787,7 +1785,7 @@ static int _regmap_raw_multi_reg_write(struct regmap *map, for (i = 0; i < num_regs; i++) { int reg = regs[i].reg; - trace_regmap_hw_write_done(map->dev, reg, 1); + trace_regmap_hw_write_done(map, reg, 1); } return ret; } @@ -2058,15 +2056,13 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, */ u8[0] |= map->read_flag_mask; - trace_regmap_hw_read_start(map->dev, reg, - val_len / map->format.val_bytes); + trace_regmap_hw_read_start(map, reg, val_len / map->format.val_bytes); ret = map->bus->read(map->bus_context, map->work_buf, map->format.reg_bytes + map->format.pad_bytes, val, val_len); - trace_regmap_hw_read_done(map->dev, reg, - val_len / map->format.val_bytes); + trace_regmap_hw_read_done(map, reg, val_len / map->format.val_bytes); return ret; } @@ -2122,7 +2118,7 @@ static int _regmap_read(struct regmap *map, unsigned int reg, dev_info(map->dev, "%x => %x\n", reg, *val); #endif - trace_regmap_reg_read(map->dev, reg, *val); + trace_regmap_reg_read(map, reg, *val); if (!map->cache_bypass) regcache_write(map, reg, *val); @@ -2479,7 +2475,7 @@ void regmap_async_complete_cb(struct regmap_async *async, int ret) struct regmap *map = async->map; bool wake; - trace_regmap_async_io_complete(map->dev); + trace_regmap_async_io_complete(map); spin_lock(&map->async_lock); list_move(&async->list, &map->async_free); @@ -2524,7 +2520,7 @@ int regmap_async_complete(struct regmap *map) if (!map->bus || !map->bus->async_write) return 0; - trace_regmap_async_complete_start(map->dev); + trace_regmap_async_complete_start(map); wait_event(map->async_waitq, regmap_async_is_done(map)); @@ -2533,7 +2529,7 @@ int regmap_async_complete(struct regmap *map) map->async_ret = 0; spin_unlock_irqrestore(&map->async_lock, flags); - trace_regmap_async_complete_done(map->dev); + trace_regmap_async_complete_done(map); return ret; } diff --git a/include/trace/events/regmap.h b/include/trace/events/regmap.h index 23d561512f64fe..22317d2b52abcb 100644 --- a/include/trace/events/regmap.h +++ b/include/trace/events/regmap.h @@ -7,27 +7,26 @@ #include #include -struct device; -struct regmap; +#include "../../../drivers/base/regmap/internal.h" /* * Log register events */ DECLARE_EVENT_CLASS(regmap_reg, - TP_PROTO(struct device *dev, unsigned int reg, + TP_PROTO(struct regmap *map, unsigned int reg, unsigned int val), - TP_ARGS(dev, reg, val), + TP_ARGS(map, reg, val), TP_STRUCT__entry( - __string( name, dev_name(dev) ) - __field( unsigned int, reg ) - __field( unsigned int, val ) + __string( name, regmap_name(map) ) + __field( unsigned int, reg ) + __field( unsigned int, val ) ), TP_fast_assign( - __assign_str(name, dev_name(dev)); + __assign_str(name, regmap_name(map)); __entry->reg = reg; __entry->val = val; ), @@ -39,45 +38,45 @@ DECLARE_EVENT_CLASS(regmap_reg, DEFINE_EVENT(regmap_reg, regmap_reg_write, - TP_PROTO(struct device *dev, unsigned int reg, + TP_PROTO(struct regmap *map, unsigned int reg, unsigned int val), - TP_ARGS(dev, reg, val) + TP_ARGS(map, reg, val) ); DEFINE_EVENT(regmap_reg, regmap_reg_read, - TP_PROTO(struct device *dev, unsigned int reg, + TP_PROTO(struct regmap *map, unsigned int reg, unsigned int val), - TP_ARGS(dev, reg, val) + TP_ARGS(map, reg, val) ); DEFINE_EVENT(regmap_reg, regmap_reg_read_cache, - TP_PROTO(struct device *dev, unsigned int reg, + TP_PROTO(struct regmap *map, unsigned int reg, unsigned int val), - TP_ARGS(dev, reg, val) + TP_ARGS(map, reg, val) ); DECLARE_EVENT_CLASS(regmap_block, - TP_PROTO(struct device *dev, unsigned int reg, int count), + TP_PROTO(struct regmap *map, unsigned int reg, int count), - TP_ARGS(dev, reg, count), + TP_ARGS(map, reg, count), TP_STRUCT__entry( - __string( name, dev_name(dev) ) - __field( unsigned int, reg ) - __field( int, count ) + __string( name, regmap_name(map) ) + __field( unsigned int, reg ) + __field( int, count ) ), TP_fast_assign( - __assign_str(name, dev_name(dev)); + __assign_str(name, regmap_name(map)); __entry->reg = reg; __entry->count = count; ), @@ -89,48 +88,48 @@ DECLARE_EVENT_CLASS(regmap_block, DEFINE_EVENT(regmap_block, regmap_hw_read_start, - TP_PROTO(struct device *dev, unsigned int reg, int count), + TP_PROTO(struct regmap *map, unsigned int reg, int count), - TP_ARGS(dev, reg, count) + TP_ARGS(map, reg, count) ); DEFINE_EVENT(regmap_block, regmap_hw_read_done, - TP_PROTO(struct device *dev, unsigned int reg, int count), + TP_PROTO(struct regmap *map, unsigned int reg, int count), - TP_ARGS(dev, reg, count) + TP_ARGS(map, reg, count) ); DEFINE_EVENT(regmap_block, regmap_hw_write_start, - TP_PROTO(struct device *dev, unsigned int reg, int count), + TP_PROTO(struct regmap *map, unsigned int reg, int count), - TP_ARGS(dev, reg, count) + TP_ARGS(map, reg, count) ); DEFINE_EVENT(regmap_block, regmap_hw_write_done, - TP_PROTO(struct device *dev, unsigned int reg, int count), + TP_PROTO(struct regmap *map, unsigned int reg, int count), - TP_ARGS(dev, reg, count) + TP_ARGS(map, reg, count) ); TRACE_EVENT(regcache_sync, - TP_PROTO(struct device *dev, const char *type, + TP_PROTO(struct regmap *map, const char *type, const char *status), - TP_ARGS(dev, type, status), + TP_ARGS(map, type, status), TP_STRUCT__entry( - __string( name, dev_name(dev) ) - __string( status, status ) - __string( type, type ) - __field( int, type ) + __string( name, regmap_name(map) ) + __string( status, status ) + __string( type, type ) + __field( int, type ) ), TP_fast_assign( - __assign_str(name, dev_name(dev)); + __assign_str(name, regmap_name(map)); __assign_str(status, status); __assign_str(type, type); ), @@ -141,17 +140,17 @@ TRACE_EVENT(regcache_sync, DECLARE_EVENT_CLASS(regmap_bool, - TP_PROTO(struct device *dev, bool flag), + TP_PROTO(struct regmap *map, bool flag), - TP_ARGS(dev, flag), + TP_ARGS(map, flag), TP_STRUCT__entry( - __string( name, dev_name(dev) ) - __field( int, flag ) + __string( name, regmap_name(map) ) + __field( int, flag ) ), TP_fast_assign( - __assign_str(name, dev_name(dev)); + __assign_str(name, regmap_name(map)); __entry->flag = flag; ), @@ -161,32 +160,32 @@ DECLARE_EVENT_CLASS(regmap_bool, DEFINE_EVENT(regmap_bool, regmap_cache_only, - TP_PROTO(struct device *dev, bool flag), + TP_PROTO(struct regmap *map, bool flag), - TP_ARGS(dev, flag) + TP_ARGS(map, flag) ); DEFINE_EVENT(regmap_bool, regmap_cache_bypass, - TP_PROTO(struct device *dev, bool flag), + TP_PROTO(struct regmap *map, bool flag), - TP_ARGS(dev, flag) + TP_ARGS(map, flag) ); DECLARE_EVENT_CLASS(regmap_async, - TP_PROTO(struct device *dev), + TP_PROTO(struct regmap *map), - TP_ARGS(dev), + TP_ARGS(map), TP_STRUCT__entry( - __string( name, dev_name(dev) ) + __string( name, regmap_name(map) ) ), TP_fast_assign( - __assign_str(name, dev_name(dev)); + __assign_str(name, regmap_name(map)); ), TP_printk("%s", __get_str(name)) @@ -194,50 +193,50 @@ DECLARE_EVENT_CLASS(regmap_async, DEFINE_EVENT(regmap_block, regmap_async_write_start, - TP_PROTO(struct device *dev, unsigned int reg, int count), + TP_PROTO(struct regmap *map, unsigned int reg, int count), - TP_ARGS(dev, reg, count) + TP_ARGS(map, reg, count) ); DEFINE_EVENT(regmap_async, regmap_async_io_complete, - TP_PROTO(struct device *dev), + TP_PROTO(struct regmap *map), - TP_ARGS(dev) + TP_ARGS(map) ); DEFINE_EVENT(regmap_async, regmap_async_complete_start, - TP_PROTO(struct device *dev), + TP_PROTO(struct regmap *map), - TP_ARGS(dev) + TP_ARGS(map) ); DEFINE_EVENT(regmap_async, regmap_async_complete_done, - TP_PROTO(struct device *dev), + TP_PROTO(struct regmap *map), - TP_ARGS(dev) + TP_ARGS(map) ); TRACE_EVENT(regcache_drop_region, - TP_PROTO(struct device *dev, unsigned int from, + TP_PROTO(struct regmap *map, unsigned int from, unsigned int to), - TP_ARGS(dev, from, to), + TP_ARGS(map, from, to), TP_STRUCT__entry( - __string( name, dev_name(dev) ) - __field( unsigned int, from ) - __field( unsigned int, to ) + __string( name, regmap_name(map) ) + __field( unsigned int, from ) + __field( unsigned int, to ) ), TP_fast_assign( - __assign_str(name, dev_name(dev)); + __assign_str(name, regmap_name(map)); __entry->from = from; __entry->to = to; ), From 954ca149d24600a805522da54c9b5f91dd631035 Mon Sep 17 00:00:00 2001 From: Yongbae Park Date: Tue, 3 Mar 2015 19:46:49 +0900 Subject: [PATCH 508/788] clocksource: efm32: Fix a NULL pointer dereference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 7b8f10da3bf1056546133c9f54f49ce389fd95ab upstream. The initialisation of the efm32 clocksource first sets up the irq and only after that initialises the data needed for irq handling. In case this initialisation is delayed the irq handler would dereference a NULL pointer. I'm not aware of anything that could delay the process in such a way, but it's better to be safe than sorry, so setup the irq only when the clock event device is ready. Acked-by: Uwe Kleine-König Signed-off-by: Yongbae Park Signed-off-by: Daniel Lezcano Signed-off-by: Greg Kroah-Hartman --- drivers/clocksource/time-efm32.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clocksource/time-efm32.c b/drivers/clocksource/time-efm32.c index bba62f9deefbd0..ec57ba2bbd87ac 100644 --- a/drivers/clocksource/time-efm32.c +++ b/drivers/clocksource/time-efm32.c @@ -225,12 +225,12 @@ static int __init efm32_clockevent_init(struct device_node *np) clock_event_ddata.base = base; clock_event_ddata.periodic_top = DIV_ROUND_CLOSEST(rate, 1024 * HZ); - setup_irq(irq, &efm32_clock_event_irq); - clockevents_config_and_register(&clock_event_ddata.evtdev, DIV_ROUND_CLOSEST(rate, 1024), 0xf, 0xffff); + setup_irq(irq, &efm32_clock_event_irq); + return 0; err_get_irq: From 4a27f58b25f1ba886c2564fb7533ba5a36c10d15 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 25 Feb 2015 16:21:03 +0300 Subject: [PATCH 509/788] tcm_fc: missing curly braces in ft_invl_hw_context() commit d556546e7ecd9fca199df4698943024d40044f8e upstream. This patch adds a missing set of conditional check braces in ft_invl_hw_context() originally introduced by commit dcd998ccd when handling DDP failures in ft_recv_write_data() code. commit dcd998ccdbf74a7d8fe0f0a44e85da1ed5975946 Author: Kiran Patil Date: Wed Aug 3 09:20:01 2011 +0000 tcm_fc: Handle DDP/SW fc_frame_payload_get failures in ft_recv_write_data Signed-off-by: Dan Carpenter Cc: Kiran Patil Signed-off-by: Nicholas Bellinger Signed-off-by: Greg Kroah-Hartman --- drivers/target/tcm_fc/tfc_io.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/target/tcm_fc/tfc_io.c b/drivers/target/tcm_fc/tfc_io.c index 97b486c3dda136..583e755d809177 100644 --- a/drivers/target/tcm_fc/tfc_io.c +++ b/drivers/target/tcm_fc/tfc_io.c @@ -359,7 +359,7 @@ void ft_invl_hw_context(struct ft_cmd *cmd) ep = fc_seq_exch(seq); if (ep) { lport = ep->lp; - if (lport && (ep->xid <= lport->lro_xid)) + if (lport && (ep->xid <= lport->lro_xid)) { /* * "ddp_done" trigger invalidation of HW * specific DDP context @@ -374,6 +374,7 @@ void ft_invl_hw_context(struct ft_cmd *cmd) * identified using ep->xid) */ cmd->was_ddp_setup = 0; + } } } } From 74fbe5657a8619d2a8fe4858ff8acf2fd788fa65 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Thu, 19 Mar 2015 22:25:16 -0700 Subject: [PATCH 510/788] tcm_qla2xxx: Fix incorrect use of __transport_register_session commit 75c3d0bf9caebb502e96683b2bc37f9692437e68 upstream. This patch fixes the incorrect use of __transport_register_session() in tcm_qla2xxx_check_initiator_node_acl() code, that does not perform explicit se_tpg->session_lock when accessing se_tpg->tpg_sess_list to add new se_sess nodes. Given that tcm_qla2xxx_check_initiator_node_acl() is not called with qla_hw->hardware_lock held for all accesses of ->tpg_sess_list, the code should be using transport_register_session() instead. Signed-off-by: Bart Van Assche Cc: Giridhar Malavali Cc: Quinn Tran Signed-off-by: Nicholas Bellinger Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/qla2xxx/tcm_qla2xxx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c index 73f9feecda72b7..272a2646a75987 100644 --- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c +++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c @@ -1598,7 +1598,7 @@ static int tcm_qla2xxx_check_initiator_node_acl( /* * Finally register the new FC Nexus with TCM */ - __transport_register_session(se_nacl->se_tpg, se_nacl, se_sess, sess); + transport_register_session(se_nacl->se_tpg, se_nacl, se_sess, sess); return 0; } From ba1615129fe8143c87487b211e64f94e1ff116ff Mon Sep 17 00:00:00 2001 From: Daniel Martin Date: Sun, 8 Mar 2015 22:27:37 -0700 Subject: [PATCH 511/788] Input: synaptics - split synaptics_resolution(), query first commit 8b04baba10b007f8b6c245a50be73cf09cc3a414 upstream. Split the function synaptics_resolution() into synaptics_resolution() and synaptics_quirks(). synaptics_resolution() will be called before synaptics_quirks() to query dimensions and resolutions before overwriting them with quirks. Signed-off-by: Daniel Martin Acked-by: Hans de Goede Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/mouse/synaptics.c | 35 ++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 23e26e0768b54a..b501dda75dcb22 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -343,7 +343,6 @@ static int synaptics_resolution(struct psmouse *psmouse) { struct synaptics_data *priv = psmouse->private; unsigned char resp[3]; - int i; if (SYN_ID_MAJOR(priv->identity) < 4) return 0; @@ -355,17 +354,6 @@ static int synaptics_resolution(struct psmouse *psmouse) } } - for (i = 0; min_max_pnpid_table[i].pnp_ids; i++) { - if (psmouse_matches_pnp_id(psmouse, - min_max_pnpid_table[i].pnp_ids)) { - priv->x_min = min_max_pnpid_table[i].x_min; - priv->x_max = min_max_pnpid_table[i].x_max; - priv->y_min = min_max_pnpid_table[i].y_min; - priv->y_max = min_max_pnpid_table[i].y_max; - return 0; - } - } - if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 5 && SYN_CAP_MAX_DIMENSIONS(priv->ext_cap_0c)) { if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_MAX_COORDS, resp)) { @@ -391,6 +379,27 @@ static int synaptics_resolution(struct psmouse *psmouse) return 0; } +/* + * Apply quirk(s) if the hardware matches + */ + +static void synaptics_apply_quirks(struct psmouse *psmouse) +{ + struct synaptics_data *priv = psmouse->private; + int i; + + for (i = 0; min_max_pnpid_table[i].pnp_ids; i++) { + if (psmouse_matches_pnp_id(psmouse, + min_max_pnpid_table[i].pnp_ids)) { + priv->x_min = min_max_pnpid_table[i].x_min; + priv->x_max = min_max_pnpid_table[i].x_max; + priv->y_min = min_max_pnpid_table[i].y_min; + priv->y_max = min_max_pnpid_table[i].y_max; + break; + } + } +} + static int synaptics_query_hardware(struct psmouse *psmouse) { if (synaptics_identify(psmouse)) @@ -406,6 +415,8 @@ static int synaptics_query_hardware(struct psmouse *psmouse) if (synaptics_resolution(psmouse)) return -1; + synaptics_apply_quirks(psmouse); + return 0; } From d41073ae4e91fb7b5ddf9df11209f3ddd5dd310c Mon Sep 17 00:00:00 2001 From: Daniel Martin Date: Sun, 8 Mar 2015 22:28:29 -0700 Subject: [PATCH 512/788] Input: synaptics - log queried and quirked dimension values commit 9aff65982d0f58a78a27769fba7e97bc937b2593 upstream. Logging the dimension values we queried and the values we use from a quirk to overwrite can be helpful for debugging. This partly relates to bug: https://bugzilla.kernel.org/show_bug.cgi?id=91541 Signed-off-by: Daniel Martin Acked-by: Hans de Goede Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/mouse/synaptics.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index b501dda75dcb22..47c5dca20a60e6 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -362,6 +362,9 @@ static int synaptics_resolution(struct psmouse *psmouse) } else { priv->x_max = (resp[0] << 5) | ((resp[1] & 0x0f) << 1); priv->y_max = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3); + psmouse_info(psmouse, + "queried max coordinates: x [..%d], y [..%d]\n", + priv->x_max, priv->y_max); } } @@ -373,6 +376,9 @@ static int synaptics_resolution(struct psmouse *psmouse) } else { priv->x_min = (resp[0] << 5) | ((resp[1] & 0x0f) << 1); priv->y_min = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3); + psmouse_info(psmouse, + "queried min coordinates: x [%d..], y [%d..]\n", + priv->x_min, priv->y_min); } } @@ -395,6 +401,10 @@ static void synaptics_apply_quirks(struct psmouse *psmouse) priv->x_max = min_max_pnpid_table[i].x_max; priv->y_min = min_max_pnpid_table[i].y_min; priv->y_max = min_max_pnpid_table[i].y_max; + psmouse_info(psmouse, + "quirked min/max coordinates: x [%d..%d], y [%d..%d]\n", + priv->x_min, priv->x_max, + priv->y_min, priv->y_max); break; } } From d02f5eae65a016b5d4301c30498a049a85312e22 Mon Sep 17 00:00:00 2001 From: Daniel Martin Date: Sun, 8 Mar 2015 22:28:40 -0700 Subject: [PATCH 513/788] Input: synaptics - query min dimensions for fw v8.1 commit ac097930f0730a9b777737de2b51e0fc49d2be7a upstream. Query the min dimensions even if the check SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 7 fails, but we know that the firmware version 8.1 is safe. With that we don't need quirks for post-2013 models anymore as they expose correct min and max dimensions. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=91541 Signed-off-by: Daniel Martin re-order the tests to check SYN_CAP_MIN_DIMENSIONS even on FW 8.1 Signed-off-by: Benjamin Tissoires Acked-by: Hans de Goede Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/mouse/synaptics.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 47c5dca20a60e6..87c37f745b9296 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -368,8 +368,14 @@ static int synaptics_resolution(struct psmouse *psmouse) } } - if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 7 && - SYN_CAP_MIN_DIMENSIONS(priv->ext_cap_0c)) { + if (SYN_CAP_MIN_DIMENSIONS(priv->ext_cap_0c) && + (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 7 || + /* + * Firmware v8.1 does not report proper number of extended + * capabilities, but has been proven to report correct min + * coordinates. + */ + SYN_ID_FULL(priv->identity) == 0x801)) { if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_MIN_COORDS, resp)) { psmouse_warn(psmouse, "device claims to have min coordinates query, but I'm not able to read it.\n"); From 7fd85998b33986ffb635c14a3f36aa9ad31fcfbf Mon Sep 17 00:00:00 2001 From: Daniel Martin Date: Sun, 8 Mar 2015 22:29:07 -0700 Subject: [PATCH 514/788] Input: synaptics - remove obsolete min/max quirk for X240 commit b05f4d1c332a22f98c037fa64f249aa30877adaf upstream. The firmware of the X240 (LEN0035, 2013/12) exposes the same values x [1232..5710], y [1156..4696] as the quirk applies. Signed-off-by: Daniel Martin Acked-by: Hans de Goede Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/mouse/synaptics.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 87c37f745b9296..af686a82b02b80 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -131,7 +131,7 @@ static const struct min_max_quirk min_max_pnpid_table[] = { 1024, 5052, 2258, 4832 }, { - (const char * const []){"LEN0035", "LEN0042", NULL}, + (const char * const []){"LEN0042", NULL}, 1232, 5710, 1156, 4696 }, { From c45a7115d592183cfe4e1d1319cd0cebc97b4c1d Mon Sep 17 00:00:00 2001 From: Daniel Martin Date: Sun, 8 Mar 2015 22:29:15 -0700 Subject: [PATCH 515/788] Input: synaptics - support min/max board id in min_max_pnpid_table commit 5b3089ddb540401c1ad2e385a03d7e89ff954585 upstream. Add a min/max range for board ids to the min/max coordinates quirk. This makes it possible to restrict quirks to specific models based upon their board id. The define ANY_BOARD_ID (0) serves as a wild card. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=91541 Signed-off-by: Daniel Martin Acked-by: Hans de Goede Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/mouse/synaptics.c | 42 +++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index af686a82b02b80..a900a385e5c338 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -120,32 +120,41 @@ void synaptics_reset(struct psmouse *psmouse) static bool cr48_profile_sensor; +#define ANY_BOARD_ID 0 struct min_max_quirk { const char * const *pnp_ids; + struct { + unsigned long int min, max; + } board_id; int x_min, x_max, y_min, y_max; }; static const struct min_max_quirk min_max_pnpid_table[] = { { (const char * const []){"LEN0033", NULL}, + {ANY_BOARD_ID, ANY_BOARD_ID}, 1024, 5052, 2258, 4832 }, { (const char * const []){"LEN0042", NULL}, + {ANY_BOARD_ID, ANY_BOARD_ID}, 1232, 5710, 1156, 4696 }, { (const char * const []){"LEN0034", "LEN0036", "LEN0037", "LEN0039", "LEN2002", "LEN2004", NULL}, + {ANY_BOARD_ID, ANY_BOARD_ID}, 1024, 5112, 2024, 4832 }, { (const char * const []){"LEN2001", NULL}, + {ANY_BOARD_ID, ANY_BOARD_ID}, 1024, 5022, 2508, 4832 }, { (const char * const []){"LEN2006", NULL}, + {ANY_BOARD_ID, ANY_BOARD_ID}, 1264, 5675, 1171, 4688 }, { } @@ -401,18 +410,27 @@ static void synaptics_apply_quirks(struct psmouse *psmouse) int i; for (i = 0; min_max_pnpid_table[i].pnp_ids; i++) { - if (psmouse_matches_pnp_id(psmouse, - min_max_pnpid_table[i].pnp_ids)) { - priv->x_min = min_max_pnpid_table[i].x_min; - priv->x_max = min_max_pnpid_table[i].x_max; - priv->y_min = min_max_pnpid_table[i].y_min; - priv->y_max = min_max_pnpid_table[i].y_max; - psmouse_info(psmouse, - "quirked min/max coordinates: x [%d..%d], y [%d..%d]\n", - priv->x_min, priv->x_max, - priv->y_min, priv->y_max); - break; - } + if (!psmouse_matches_pnp_id(psmouse, + min_max_pnpid_table[i].pnp_ids)) + continue; + + if (min_max_pnpid_table[i].board_id.min != ANY_BOARD_ID && + priv->board_id < min_max_pnpid_table[i].board_id.min) + continue; + + if (min_max_pnpid_table[i].board_id.max != ANY_BOARD_ID && + priv->board_id > min_max_pnpid_table[i].board_id.max) + continue; + + priv->x_min = min_max_pnpid_table[i].x_min; + priv->x_max = min_max_pnpid_table[i].x_max; + priv->y_min = min_max_pnpid_table[i].y_min; + priv->y_max = min_max_pnpid_table[i].y_max; + psmouse_info(psmouse, + "quirked min/max coordinates: x [%d..%d], y [%d..%d]\n", + priv->x_min, priv->x_max, + priv->y_min, priv->y_max); + break; } } From abadfea64408020361eb6a76b1c930e4418ee146 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Sun, 8 Mar 2015 22:29:25 -0700 Subject: [PATCH 516/788] Input: synaptics - skip quirks when post-2013 dimensions commit 02e07492cdfae9c86e3bd21c0beec88dbcc1e9e8 upstream. Post-2013 Lenovo laptops provide correct min/max dimensions, which are different with the ones currently quirked. According to https://bugzilla.kernel.org/show_bug.cgi?id=91541 the following board ids are assigned in the post-2013 touchpads: t440p/t440s: LEN0036 -> 2964/2962 t540p: LEN0034 -> 2964 Using 2961 as the common minimum makes these 3 laptops OK. We may need to update those values later if other pnp_ids has a lower board_id. Signed-off-by: Benjamin Tissoires Acked-by: Hans de Goede Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/mouse/synaptics.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index a900a385e5c338..9567a708aa6437 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -144,7 +144,7 @@ static const struct min_max_quirk min_max_pnpid_table[] = { (const char * const []){"LEN0034", "LEN0036", "LEN0037", "LEN0039", "LEN2002", "LEN2004", NULL}, - {ANY_BOARD_ID, ANY_BOARD_ID}, + {ANY_BOARD_ID, 2961}, 1024, 5112, 2024, 4832 }, { From 6b2b666d03bb7e14c93e194b262b030a85a1b632 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 8 Mar 2015 22:30:43 -0700 Subject: [PATCH 517/788] Input: synaptics - fix middle button on Lenovo 2015 products commit dc5465dc8a6d5cae8a0e1d8826bdcb2e4cb261ab upstream. On the X1 Carbon 3rd gen (with a 2015 broadwell cpu), the physical middle button of the trackstick (attached to the touchpad serio device, of course) seems to get lost. Actually, the touchpads reports 3 extra buttons, which falls in the switch below to the '2' case. Let's handle the case of odd numbers also, so that the middle button finds its way back. Signed-off-by: Benjamin Tissoires Acked-by: Hans de Goede Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/mouse/synaptics.c | 44 ++++++++++++++++----------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 9567a708aa6437..e78cc55785271e 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -658,6 +658,18 @@ static void synaptics_parse_agm(const unsigned char buf[], priv->agm_pending = true; } +static void synaptics_parse_ext_buttons(const unsigned char buf[], + struct synaptics_data *priv, + struct synaptics_hw_state *hw) +{ + unsigned int ext_bits = + (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) + 1) >> 1; + unsigned int ext_mask = GENMASK(ext_bits - 1, 0); + + hw->ext_buttons = buf[4] & ext_mask; + hw->ext_buttons |= (buf[5] & ext_mask) << ext_bits; +} + static bool is_forcepad; static int synaptics_parse_hw_state(const unsigned char buf[], @@ -744,28 +756,9 @@ static int synaptics_parse_hw_state(const unsigned char buf[], hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0; } - if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) && + if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) > 0 && ((buf[0] ^ buf[3]) & 0x02)) { - switch (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) { - default: - /* - * if nExtBtn is greater than 8 it should be - * considered invalid and treated as 0 - */ - break; - case 8: - hw->ext_buttons |= ((buf[5] & 0x08)) ? 0x80 : 0; - hw->ext_buttons |= ((buf[4] & 0x08)) ? 0x40 : 0; - case 6: - hw->ext_buttons |= ((buf[5] & 0x04)) ? 0x20 : 0; - hw->ext_buttons |= ((buf[4] & 0x04)) ? 0x10 : 0; - case 4: - hw->ext_buttons |= ((buf[5] & 0x02)) ? 0x08 : 0; - hw->ext_buttons |= ((buf[4] & 0x02)) ? 0x04 : 0; - case 2: - hw->ext_buttons |= ((buf[5] & 0x01)) ? 0x02 : 0; - hw->ext_buttons |= ((buf[4] & 0x01)) ? 0x01 : 0; - } + synaptics_parse_ext_buttons(buf, priv, hw); } } else { hw->x = (((buf[1] & 0x1f) << 8) | buf[2]); @@ -832,6 +825,7 @@ static void synaptics_report_buttons(struct psmouse *psmouse, { struct input_dev *dev = psmouse->dev; struct synaptics_data *priv = psmouse->private; + int ext_bits = (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) + 1) >> 1; int i; input_report_key(dev, BTN_LEFT, hw->left); @@ -845,8 +839,12 @@ static void synaptics_report_buttons(struct psmouse *psmouse, input_report_key(dev, BTN_BACK, hw->down); } - for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++) - input_report_key(dev, BTN_0 + i, hw->ext_buttons & (1 << i)); + for (i = 0; i < ext_bits; i++) { + input_report_key(dev, BTN_0 + 2 * i, + hw->ext_buttons & (1 << i)); + input_report_key(dev, BTN_1 + 2 * i, + hw->ext_buttons & (1 << (i + ext_bits))); + } } static void synaptics_report_slot(struct input_dev *dev, int slot, From e5a147570ddbbcee6d7cae592e364575d98e15f8 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Sun, 8 Mar 2015 22:32:43 -0700 Subject: [PATCH 518/788] Input: synaptics - handle spurious release of trackstick buttons commit ebc80840b850db72f7ae84fbcf77630ae5409629 upstream. The Fimware 8.1 has a bug in which the extra buttons are only sent when the ExtBit is 1. This should be fixed in a future FW update which should have a bump of the minor version. Signed-off-by: Benjamin Tissoires Acked-by: Hans de Goede Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/mouse/synaptics.c | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index e78cc55785271e..2f42a712f3e051 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -820,14 +820,36 @@ static void synaptics_report_semi_mt_data(struct input_dev *dev, } } -static void synaptics_report_buttons(struct psmouse *psmouse, - const struct synaptics_hw_state *hw) +static void synaptics_report_ext_buttons(struct psmouse *psmouse, + const struct synaptics_hw_state *hw) { struct input_dev *dev = psmouse->dev; struct synaptics_data *priv = psmouse->private; int ext_bits = (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) + 1) >> 1; int i; + if (!SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap)) + return; + + /* Bug in FW 8.1, buttons are reported only when ExtBit is 1 */ + if (SYN_ID_FULL(priv->identity) == 0x801 && + !((psmouse->packet[0] ^ psmouse->packet[3]) & 0x02)) + return; + + for (i = 0; i < ext_bits; i++) { + input_report_key(dev, BTN_0 + 2 * i, + hw->ext_buttons & (1 << i)); + input_report_key(dev, BTN_1 + 2 * i, + hw->ext_buttons & (1 << (i + ext_bits))); + } +} + +static void synaptics_report_buttons(struct psmouse *psmouse, + const struct synaptics_hw_state *hw) +{ + struct input_dev *dev = psmouse->dev; + struct synaptics_data *priv = psmouse->private; + input_report_key(dev, BTN_LEFT, hw->left); input_report_key(dev, BTN_RIGHT, hw->right); @@ -839,12 +861,7 @@ static void synaptics_report_buttons(struct psmouse *psmouse, input_report_key(dev, BTN_BACK, hw->down); } - for (i = 0; i < ext_bits; i++) { - input_report_key(dev, BTN_0 + 2 * i, - hw->ext_buttons & (1 << i)); - input_report_key(dev, BTN_1 + 2 * i, - hw->ext_buttons & (1 << (i + ext_bits))); - } + synaptics_report_ext_buttons(psmouse, hw); } static void synaptics_report_slot(struct input_dev *dev, int slot, From fbd0f7e0c8d0fdeb8056b0a5d521af8234f62ffd Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Sun, 8 Mar 2015 22:33:36 -0700 Subject: [PATCH 519/788] Input: synaptics - do not retrieve the board id on old firmwares commit b57a7128be24062b5b5b26032b7cd58f1651547e upstream. The board id capability has been added in firmware 7.5. Signed-off-by: Benjamin Tissoires Acked-by: Hans de Goede Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/mouse/synaptics.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 2f42a712f3e051..2176874a41b116 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -250,6 +250,10 @@ static int synaptics_board_id(struct psmouse *psmouse) struct synaptics_data *priv = psmouse->private; unsigned char bid[3]; + /* firmwares prior 7.5 have no board_id encoded */ + if (SYN_ID_FULL(priv->identity) < 0x705) + return 0; + if (synaptics_send_cmd(psmouse, SYN_QUE_MODES, bid)) return -1; priv->board_id = ((bid[0] & 0xfc) << 6) | bid[1]; From 5b6fb24345ead5f763b4e80c53bd69d551d5d43a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 12 Mar 2015 08:53:27 +0200 Subject: [PATCH 520/788] nl80211: ignore HT/VHT capabilities without QoS/WMM commit 496fcc294daab18799e190c0264863d653588d1f upstream. As HT/VHT depend heavily on QoS/WMM, it's not a good idea to let userspace add clients that have HT/VHT but not QoS/WMM. Since it does so in certain cases we've observed (client is using HT IEs but not QoS/WMM) just ignore the HT/VHT info at this point and don't pass it down to the drivers which might unconditionally use it. Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- net/wireless/nl80211.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 8887c6e5fca85c..e13325f709f2be 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4360,6 +4360,16 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) if (parse_station_flags(info, dev->ieee80211_ptr->iftype, ¶ms)) return -EINVAL; + /* HT/VHT requires QoS, but if we don't have that just ignore HT/VHT + * as userspace might just pass through the capabilities from the IEs + * directly, rather than enforcing this restriction and returning an + * error in this case. + */ + if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_WME))) { + params.ht_capa = NULL; + params.vht_capa = NULL; + } + /* When you run into this, adjust the code below for the new flag */ BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 7); From 0bb81d514fbc6e1e2a68eb611b9cad7706f54546 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Tue, 10 Feb 2015 12:48:44 +0100 Subject: [PATCH 521/788] mac80211: disable u-APSD queues by default commit aa75ebc275b2a91b193654a177daf900ad6703f0 upstream. Some APs experience problems when working with U-APSD. Decreasing the probability of that happening by using legacy mode for all ACs but VO isn't enough. Cisco 4410N originally forced us to enable VO by default only because it treated non-VO ACs as legacy. However some APs (notably Netgear R7000) silently reclassify packets to different ACs. Since u-APSD ACs require trigger frames for frame retrieval clients would never see some frames (e.g. ARP responses) or would fetch them accidentally after a long time. It makes little sense to enable u-APSD queues by default because it needs userspace applications to be aware of it to actually take advantage of the possible additional powersavings. Implicitly depending on driver autotrigger frame support doesn't make much sense. Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- net/mac80211/ieee80211_i.h | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index cc6e964d98370e..fa7568ce7ef35a 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -58,13 +58,24 @@ struct ieee80211_local; #define IEEE80211_UNSET_POWER_LEVEL INT_MIN /* - * Some APs experience problems when working with U-APSD. Decrease the - * probability of that happening by using legacy mode for all ACs but VO. - * The AP that caused us trouble was a Cisco 4410N. It ignores our - * setting, and always treats non-VO ACs as legacy. + * Some APs experience problems when working with U-APSD. Decreasing the + * probability of that happening by using legacy mode for all ACs but VO isn't + * enough. + * + * Cisco 4410N originally forced us to enable VO by default only because it + * treated non-VO ACs as legacy. + * + * However some APs (notably Netgear R7000) silently reclassify packets to + * different ACs. Since u-APSD ACs require trigger frames for frame retrieval + * clients would never see some frames (e.g. ARP responses) or would fetch them + * accidentally after a long time. + * + * It makes little sense to enable u-APSD queues by default because it needs + * userspace applications to be aware of it to actually take advantage of the + * possible additional powersavings. Implicitly depending on driver autotrigger + * frame support doesn't make much sense. */ -#define IEEE80211_DEFAULT_UAPSD_QUEUES \ - IEEE80211_WMM_IE_STA_QOSINFO_AC_VO +#define IEEE80211_DEFAULT_UAPSD_QUEUES 0 #define IEEE80211_DEFAULT_MAX_SP_LEN \ IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL From d22db2725c02cc7cf374077d0fc130cf69e7a0cf Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Mon, 2 Mar 2015 14:28:52 -0500 Subject: [PATCH 522/788] mac80211: drop unencrypted frames in mesh fwding commit d0c22119f574b851e63360c6b8660fe9593bbc3c upstream. The mesh forwarding path was not checking that data frames were protected when running an encrypted network; add the necessary check. Reported-by: Johannes Berg Signed-off-by: Bob Copeland Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- net/mac80211/rx.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index d69ca513848e7e..b448e8ff32136d 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2191,6 +2191,9 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) hdr = (struct ieee80211_hdr *) skb->data; mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); + if (ieee80211_drop_unencrypted(rx, hdr->frame_control)) + return RX_DROP_MONITOR; + /* frame is in RMC, don't forward */ if (ieee80211_is_data(hdr->frame_control) && is_multicast_ether_addr(hdr->addr1) && From 89e1dc988d602f16b5239928359f5a1c6fdbb008 Mon Sep 17 00:00:00 2001 From: Andrei Otcheretianski Date: Thu, 12 Mar 2015 08:53:30 +0200 Subject: [PATCH 523/788] mac80211: count interfaces correctly for combination checks commit 0f611d28fc2e13cfec64e1c544c16a086886805a upstream. Since moving the interface combination checks to mac80211, it's broken because it now only considers interfaces with an assigned channel context, so for example any interface that isn't active can still be up, which is clearly an issue; also, in particular P2P-Device wdevs are an issue since they never have a chanctx. Fix this by counting running interfaces instead the ones with a channel context assigned. Fixes: 73de86a38962b ("cfg80211/mac80211: move interface counting for combination check to mac80211") Signed-off-by: Andrei Otcheretianski Signed-off-by: Emmanuel Grumbach [rewrite commit message, dig out the commit it fixes] Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- net/mac80211/util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 974ebe70f5b013..1ce38e71ecd8cf 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -3153,7 +3153,7 @@ int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata, wdev_iter = &sdata_iter->wdev; if (sdata_iter == sdata || - rcu_access_pointer(sdata_iter->vif.chanctx_conf) == NULL || + !ieee80211_sdata_running(sdata_iter) || local->hw.wiphy->software_iftypes & BIT(wdev_iter->iftype)) continue; From c9dd67bcfa4bd06dd6db26c380fa4255f06d5541 Mon Sep 17 00:00:00 2001 From: Jacob Pan Date: Fri, 13 Mar 2015 03:48:56 -0700 Subject: [PATCH 524/788] powercap / RAPL: handle domains with different energy units commit d474a4d365aaa5c7aabcf11a74ea43aa23f6f2e9 upstream. The current driver assumes all RAPL domains within a CPU package have the same energy unit. This is no longer true for HSW server CPUs since DRAM domain has is own fixed energy unit which can be different than the package energy unit enumerated by package power MSR. In fact, the default HSW EP package power unit is 61uJ whereas DRAM domain unit is 15.3uJ. The result is that DRAM power consumption is counted 4x more than real power reported by energy counters, similarly for max_energy_range_uj of DRAM domain. This patch adds domain specific energy unit per cpu type, it allows domain energy unit to override package energy unit if non zero. Please see this document for details. "Intel Xeon Processor E5-1600 and E5-2600 v3 Product Families, Volume 2 of 2. Datasheet, September 2014, Reference Number: 330784-001 " Signed-off-by: Jacob Pan Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/powercap/intel_rapl.c | 54 +++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/drivers/powercap/intel_rapl.c b/drivers/powercap/intel_rapl.c index 97b5e4ee1ca40a..63d4033eb68386 100644 --- a/drivers/powercap/intel_rapl.c +++ b/drivers/powercap/intel_rapl.c @@ -73,7 +73,7 @@ #define TIME_WINDOW_MAX_MSEC 40000 #define TIME_WINDOW_MIN_MSEC 250 - +#define ENERGY_UNIT_SCALE 1000 /* scale from driver unit to powercap unit */ enum unit_type { ARBITRARY_UNIT, /* no translation */ POWER_UNIT, @@ -158,6 +158,7 @@ struct rapl_domain { struct rapl_power_limit rpl[NR_POWER_LIMITS]; u64 attr_map; /* track capabilities */ unsigned int state; + unsigned int domain_energy_unit; int package_id; }; #define power_zone_to_rapl_domain(_zone) \ @@ -190,6 +191,7 @@ struct rapl_defaults { void (*set_floor_freq)(struct rapl_domain *rd, bool mode); u64 (*compute_time_window)(struct rapl_package *rp, u64 val, bool to_raw); + unsigned int dram_domain_energy_unit; }; static struct rapl_defaults *rapl_defaults; @@ -227,7 +229,8 @@ static int rapl_read_data_raw(struct rapl_domain *rd, static int rapl_write_data_raw(struct rapl_domain *rd, enum rapl_primitives prim, unsigned long long value); -static u64 rapl_unit_xlate(int package, enum unit_type type, u64 value, +static u64 rapl_unit_xlate(struct rapl_domain *rd, int package, + enum unit_type type, u64 value, int to_raw); static void package_power_limit_irq_save(int package_id); @@ -305,7 +308,9 @@ static int get_energy_counter(struct powercap_zone *power_zone, u64 *energy_raw) static int get_max_energy_counter(struct powercap_zone *pcd_dev, u64 *energy) { - *energy = rapl_unit_xlate(0, ENERGY_UNIT, ENERGY_STATUS_MASK, 0); + struct rapl_domain *rd = power_zone_to_rapl_domain(pcd_dev); + + *energy = rapl_unit_xlate(rd, 0, ENERGY_UNIT, ENERGY_STATUS_MASK, 0); return 0; } @@ -639,6 +644,11 @@ static void rapl_init_domains(struct rapl_package *rp) rd->msrs[4] = MSR_DRAM_POWER_INFO; rd->rpl[0].prim_id = PL1_ENABLE; rd->rpl[0].name = pl1_name; + rd->domain_energy_unit = + rapl_defaults->dram_domain_energy_unit; + if (rd->domain_energy_unit) + pr_info("DRAM domain energy unit %dpj\n", + rd->domain_energy_unit); break; } if (mask) { @@ -648,11 +658,13 @@ static void rapl_init_domains(struct rapl_package *rp) } } -static u64 rapl_unit_xlate(int package, enum unit_type type, u64 value, +static u64 rapl_unit_xlate(struct rapl_domain *rd, int package, + enum unit_type type, u64 value, int to_raw) { u64 units = 1; struct rapl_package *rp; + u64 scale = 1; rp = find_package_by_id(package); if (!rp) @@ -663,7 +675,12 @@ static u64 rapl_unit_xlate(int package, enum unit_type type, u64 value, units = rp->power_unit; break; case ENERGY_UNIT: - units = rp->energy_unit; + scale = ENERGY_UNIT_SCALE; + /* per domain unit takes precedence */ + if (rd && rd->domain_energy_unit) + units = rd->domain_energy_unit; + else + units = rp->energy_unit; break; case TIME_UNIT: return rapl_defaults->compute_time_window(rp, value, to_raw); @@ -673,11 +690,11 @@ static u64 rapl_unit_xlate(int package, enum unit_type type, u64 value, }; if (to_raw) - return div64_u64(value, units); + return div64_u64(value, units) * scale; value *= units; - return value; + return div64_u64(value, scale); } /* in the order of enum rapl_primitives */ @@ -773,7 +790,7 @@ static int rapl_read_data_raw(struct rapl_domain *rd, final = value & rp->mask; final = final >> rp->shift; if (xlate) - *data = rapl_unit_xlate(rd->package_id, rp->unit, final, 0); + *data = rapl_unit_xlate(rd, rd->package_id, rp->unit, final, 0); else *data = final; @@ -799,7 +816,7 @@ static int rapl_write_data_raw(struct rapl_domain *rd, "failed to read msr 0x%x on cpu %d\n", msr, cpu); return -EIO; } - value = rapl_unit_xlate(rd->package_id, rp->unit, value, 1); + value = rapl_unit_xlate(rd, rd->package_id, rp->unit, value, 1); msr_val &= ~rp->mask; msr_val |= value << rp->shift; if (wrmsrl_safe_on_cpu(cpu, msr, msr_val)) { @@ -818,7 +835,7 @@ static int rapl_write_data_raw(struct rapl_domain *rd, * calculate units differ on different CPUs. * We convert the units to below format based on CPUs. * i.e. - * energy unit: microJoules : Represented in microJoules by default + * energy unit: picoJoules : Represented in picoJoules by default * power unit : microWatts : Represented in milliWatts by default * time unit : microseconds: Represented in seconds by default */ @@ -834,7 +851,7 @@ static int rapl_check_unit_core(struct rapl_package *rp, int cpu) } value = (msr_val & ENERGY_UNIT_MASK) >> ENERGY_UNIT_OFFSET; - rp->energy_unit = 1000000 / (1 << value); + rp->energy_unit = ENERGY_UNIT_SCALE * 1000000 / (1 << value); value = (msr_val & POWER_UNIT_MASK) >> POWER_UNIT_OFFSET; rp->power_unit = 1000000 / (1 << value); @@ -842,7 +859,7 @@ static int rapl_check_unit_core(struct rapl_package *rp, int cpu) value = (msr_val & TIME_UNIT_MASK) >> TIME_UNIT_OFFSET; rp->time_unit = 1000000 / (1 << value); - pr_debug("Core CPU package %d energy=%duJ, time=%dus, power=%duW\n", + pr_debug("Core CPU package %d energy=%dpJ, time=%dus, power=%duW\n", rp->id, rp->energy_unit, rp->time_unit, rp->power_unit); return 0; @@ -859,7 +876,7 @@ static int rapl_check_unit_atom(struct rapl_package *rp, int cpu) return -ENODEV; } value = (msr_val & ENERGY_UNIT_MASK) >> ENERGY_UNIT_OFFSET; - rp->energy_unit = 1 << value; + rp->energy_unit = ENERGY_UNIT_SCALE * 1 << value; value = (msr_val & POWER_UNIT_MASK) >> POWER_UNIT_OFFSET; rp->power_unit = (1 << value) * 1000; @@ -867,7 +884,7 @@ static int rapl_check_unit_atom(struct rapl_package *rp, int cpu) value = (msr_val & TIME_UNIT_MASK) >> TIME_UNIT_OFFSET; rp->time_unit = 1000000 / (1 << value); - pr_debug("Atom package %d energy=%duJ, time=%dus, power=%duW\n", + pr_debug("Atom package %d energy=%dpJ, time=%dus, power=%duW\n", rp->id, rp->energy_unit, rp->time_unit, rp->power_unit); return 0; @@ -1017,6 +1034,13 @@ static const struct rapl_defaults rapl_defaults_core = { .compute_time_window = rapl_compute_time_window_core, }; +static const struct rapl_defaults rapl_defaults_hsw_server = { + .check_unit = rapl_check_unit_core, + .set_floor_freq = set_floor_freq_default, + .compute_time_window = rapl_compute_time_window_core, + .dram_domain_energy_unit = 15300, +}; + static const struct rapl_defaults rapl_defaults_atom = { .check_unit = rapl_check_unit_atom, .set_floor_freq = set_floor_freq_atom, @@ -1037,7 +1061,7 @@ static const struct x86_cpu_id rapl_ids[] = { RAPL_CPU(0x3a, rapl_defaults_core),/* Ivy Bridge */ RAPL_CPU(0x3c, rapl_defaults_core),/* Haswell */ RAPL_CPU(0x3d, rapl_defaults_core),/* Broadwell */ - RAPL_CPU(0x3f, rapl_defaults_core),/* Haswell */ + RAPL_CPU(0x3f, rapl_defaults_hsw_server),/* Haswell servers */ RAPL_CPU(0x45, rapl_defaults_core),/* Haswell ULT */ RAPL_CPU(0x4C, rapl_defaults_atom),/* Braswell */ RAPL_CPU(0x4A, rapl_defaults_atom),/* Tangier */ From 4fcad7fbbb9596f5e73536f80935230951ffcb16 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Mon, 2 Feb 2015 15:21:27 +0200 Subject: [PATCH 525/788] iwlwifi: mvm: rs: fix BT Coex check to look at the correct ant commit 57bff1485096c53f943e26b1c5847f2a9dfe84db upstream. The check to avoid the shared antenna was passed the wrong antenna parameter. It should have checked whether the antenna of the next column we're considering is allowed and instead it was passed the current antenna. This could lead to a wrong choice of the next column in the rs algorithm and non optimal performance. Fixes: commit 219fb66b49fac64bb ("iwlwifi: mvm: rs - don't use the shared antenna when BT load is high") Signed-off-by: Eyal Shapira Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/iwlwifi/mvm/rs.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index 30ceb67ed7a7a2..1a0327075eb81e 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -146,9 +146,12 @@ enum rs_column_mode { #define MAX_NEXT_COLUMNS 7 #define MAX_COLUMN_CHECKS 3 +struct rs_tx_column; + typedef bool (*allow_column_func_t) (struct iwl_mvm *mvm, struct ieee80211_sta *sta, - struct iwl_scale_tbl_info *tbl); + struct iwl_scale_tbl_info *tbl, + const struct rs_tx_column *next_col); struct rs_tx_column { enum rs_column_mode mode; @@ -159,13 +162,15 @@ struct rs_tx_column { }; static bool rs_ant_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - struct iwl_scale_tbl_info *tbl) + struct iwl_scale_tbl_info *tbl, + const struct rs_tx_column *next_col) { - return iwl_mvm_bt_coex_is_ant_avail(mvm, tbl->rate.ant); + return iwl_mvm_bt_coex_is_ant_avail(mvm, next_col->ant); } static bool rs_mimo_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - struct iwl_scale_tbl_info *tbl) + struct iwl_scale_tbl_info *tbl, + const struct rs_tx_column *next_col) { if (!sta->ht_cap.ht_supported) return false; @@ -183,7 +188,8 @@ static bool rs_mimo_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, } static bool rs_siso_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - struct iwl_scale_tbl_info *tbl) + struct iwl_scale_tbl_info *tbl, + const struct rs_tx_column *next_col) { if (!sta->ht_cap.ht_supported) return false; @@ -192,7 +198,8 @@ static bool rs_siso_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, } static bool rs_sgi_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - struct iwl_scale_tbl_info *tbl) + struct iwl_scale_tbl_info *tbl, + const struct rs_tx_column *next_col) { struct rs_rate *rate = &tbl->rate; struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; @@ -1594,7 +1601,7 @@ static enum rs_column rs_get_next_column(struct iwl_mvm *mvm, for (j = 0; j < MAX_COLUMN_CHECKS; j++) { allow_func = next_col->checks[j]; - if (allow_func && !allow_func(mvm, sta, tbl)) + if (allow_func && !allow_func(mvm, sta, tbl, next_col)) break; } From 3c95c66cedf559371143652334b3e568150d3b00 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 23 Feb 2015 02:40:07 +0200 Subject: [PATCH 526/788] iwlwifi: fix max_ht_ampdu_exponent for older devices commit 540623caa6c769d9d19e6044949f5fa2fe1a33a6 upstream. The commit below didn't update the max_ht_ampdu_exponent for the devices listed in iwl-[1-6]000.c which, in result, became 0 instead of 8K. This reduced the size of the Rx AMPDU from 64K to 8K which had an impact in the Rx throughput. One user reported that because of this, his downstream throughput droppped by a half. Fixes: c064ddf318aa ("iwlwifi: change max HT and VHT A-MPDU exponent") Reported-and-tested-by: Valentin Manea Signed-off-by: Emmanuel Grumbach Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/iwlwifi/iwl-1000.c | 6 ++++-- drivers/net/wireless/iwlwifi/iwl-2000.c | 13 +++++++++---- drivers/net/wireless/iwlwifi/iwl-5000.c | 6 ++++-- drivers/net/wireless/iwlwifi/iwl-6000.c | 18 ++++++++++++------ 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c index c3817fae16c042..06f6cc08f451ca 100644 --- a/drivers/net/wireless/iwlwifi/iwl-1000.c +++ b/drivers/net/wireless/iwlwifi/iwl-1000.c @@ -95,7 +95,8 @@ static const struct iwl_eeprom_params iwl1000_eeprom_params = { .nvm_calib_ver = EEPROM_1000_TX_POWER_VERSION, \ .base_params = &iwl1000_base_params, \ .eeprom_params = &iwl1000_eeprom_params, \ - .led_mode = IWL_LED_BLINK + .led_mode = IWL_LED_BLINK, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K const struct iwl_cfg iwl1000_bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 1000 BGN", @@ -121,7 +122,8 @@ const struct iwl_cfg iwl1000_bg_cfg = { .base_params = &iwl1000_base_params, \ .eeprom_params = &iwl1000_eeprom_params, \ .led_mode = IWL_LED_RF_STATE, \ - .rx_with_siso_diversity = true + .rx_with_siso_diversity = true, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K const struct iwl_cfg iwl100_bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 100 BGN", diff --git a/drivers/net/wireless/iwlwifi/iwl-2000.c b/drivers/net/wireless/iwlwifi/iwl-2000.c index 21e5d0843a62a8..890b95f497d6ec 100644 --- a/drivers/net/wireless/iwlwifi/iwl-2000.c +++ b/drivers/net/wireless/iwlwifi/iwl-2000.c @@ -123,7 +123,9 @@ static const struct iwl_eeprom_params iwl20x0_eeprom_params = { .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ .base_params = &iwl2000_base_params, \ .eeprom_params = &iwl20x0_eeprom_params, \ - .led_mode = IWL_LED_RF_STATE + .led_mode = IWL_LED_RF_STATE, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + const struct iwl_cfg iwl2000_2bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 2200 BGN", @@ -149,7 +151,8 @@ const struct iwl_cfg iwl2000_2bgn_d_cfg = { .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ .base_params = &iwl2030_base_params, \ .eeprom_params = &iwl20x0_eeprom_params, \ - .led_mode = IWL_LED_RF_STATE + .led_mode = IWL_LED_RF_STATE, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K const struct iwl_cfg iwl2030_2bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 2230 BGN", @@ -170,7 +173,8 @@ const struct iwl_cfg iwl2030_2bgn_cfg = { .base_params = &iwl2000_base_params, \ .eeprom_params = &iwl20x0_eeprom_params, \ .led_mode = IWL_LED_RF_STATE, \ - .rx_with_siso_diversity = true + .rx_with_siso_diversity = true, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K const struct iwl_cfg iwl105_bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 105 BGN", @@ -197,7 +201,8 @@ const struct iwl_cfg iwl105_bgn_d_cfg = { .base_params = &iwl2030_base_params, \ .eeprom_params = &iwl20x0_eeprom_params, \ .led_mode = IWL_LED_RF_STATE, \ - .rx_with_siso_diversity = true + .rx_with_siso_diversity = true, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K const struct iwl_cfg iwl135_bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 135 BGN", diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index 332bbede39e5b0..724194e234141e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -93,7 +93,8 @@ static const struct iwl_eeprom_params iwl5000_eeprom_params = { .nvm_calib_ver = EEPROM_5000_TX_POWER_VERSION, \ .base_params = &iwl5000_base_params, \ .eeprom_params = &iwl5000_eeprom_params, \ - .led_mode = IWL_LED_BLINK + .led_mode = IWL_LED_BLINK, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K const struct iwl_cfg iwl5300_agn_cfg = { .name = "Intel(R) Ultimate N WiFi Link 5300 AGN", @@ -158,7 +159,8 @@ const struct iwl_cfg iwl5350_agn_cfg = { .base_params = &iwl5000_base_params, \ .eeprom_params = &iwl5000_eeprom_params, \ .led_mode = IWL_LED_BLINK, \ - .internal_wimax_coex = true + .internal_wimax_coex = true, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K const struct iwl_cfg iwl5150_agn_cfg = { .name = "Intel(R) WiMAX/WiFi Link 5150 AGN", diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c index 8f2c3c8c6b843f..21b2630763dc93 100644 --- a/drivers/net/wireless/iwlwifi/iwl-6000.c +++ b/drivers/net/wireless/iwlwifi/iwl-6000.c @@ -145,7 +145,8 @@ static const struct iwl_eeprom_params iwl6000_eeprom_params = { .nvm_calib_ver = EEPROM_6005_TX_POWER_VERSION, \ .base_params = &iwl6000_g2_base_params, \ .eeprom_params = &iwl6000_eeprom_params, \ - .led_mode = IWL_LED_RF_STATE + .led_mode = IWL_LED_RF_STATE, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K const struct iwl_cfg iwl6005_2agn_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6205 AGN", @@ -199,7 +200,8 @@ const struct iwl_cfg iwl6005_2agn_mow2_cfg = { .nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \ .base_params = &iwl6000_g2_base_params, \ .eeprom_params = &iwl6000_eeprom_params, \ - .led_mode = IWL_LED_RF_STATE + .led_mode = IWL_LED_RF_STATE, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K const struct iwl_cfg iwl6030_2agn_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6230 AGN", @@ -235,7 +237,8 @@ const struct iwl_cfg iwl6030_2bg_cfg = { .nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \ .base_params = &iwl6000_g2_base_params, \ .eeprom_params = &iwl6000_eeprom_params, \ - .led_mode = IWL_LED_RF_STATE + .led_mode = IWL_LED_RF_STATE, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K const struct iwl_cfg iwl6035_2agn_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6235 AGN", @@ -290,7 +293,8 @@ const struct iwl_cfg iwl130_bg_cfg = { .nvm_calib_ver = EEPROM_6000_TX_POWER_VERSION, \ .base_params = &iwl6000_base_params, \ .eeprom_params = &iwl6000_eeprom_params, \ - .led_mode = IWL_LED_BLINK + .led_mode = IWL_LED_BLINK, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K const struct iwl_cfg iwl6000i_2agn_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6200 AGN", @@ -322,7 +326,8 @@ const struct iwl_cfg iwl6000i_2bg_cfg = { .base_params = &iwl6050_base_params, \ .eeprom_params = &iwl6000_eeprom_params, \ .led_mode = IWL_LED_BLINK, \ - .internal_wimax_coex = true + .internal_wimax_coex = true, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K const struct iwl_cfg iwl6050_2agn_cfg = { .name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 AGN", @@ -347,7 +352,8 @@ const struct iwl_cfg iwl6050_2abg_cfg = { .base_params = &iwl6050_base_params, \ .eeprom_params = &iwl6000_eeprom_params, \ .led_mode = IWL_LED_BLINK, \ - .internal_wimax_coex = true + .internal_wimax_coex = true, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K const struct iwl_cfg iwl6150_bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N + WiMAX 6150 BGN", From 145962ee31829390e43a61a53008dde37d428b2f Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 5 Mar 2015 13:43:15 +0200 Subject: [PATCH 527/788] iwlwifi: mvm: BT Coex - fix a NULL pointer exception commit 4cd4b50cc2429294c23a1998c33fdfd804db0f37 upstream. The commit below introduced an unsafe dereference of mvmvif->phy_ctxt. It can be NULL even if we hold the mutex. We can be handling a BT Coex notification while the vif has already been unassigned. This can happen since the BT Coex notification is hanled asynchronuously: we can have started to handle the BT Coex notification trying to acquire the mutex while the unassign flow already got it. The BT Coex notification handling will wait for the mutext. I'll get it later, but then mvmvif->phy_ctxt will be NULL. Panic log: BUG: unable to handle kernel NULL pointer dereference at (null) IP: [] iwl_mvm_bt_notif_iterator+0x9d/0x340 [iwlmvm] *pdpt = 0000000000000000 *pde = f000eef300000007 Oops: 0000 [#1] SMP Workqueue: events iwl_mvm_async_handlers_wk [iwlmvm] task: ed719b20 ti: ec03e000 task.ti: ec03e000 EIP: 0060:[] EFLAGS: 00010202 CPU: 2 EIP is at iwl_mvm_bt_notif_iterator+0x9d/0x340 [iwlmvm] EAX: 00000000 EBX: f6d3cb70 ECX: f6d3cb70 EDX: 00000000 ESI: ec03fe40 EDI: efeb8810 EBP: ec03fdf0 ESP: ec03fdac DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068 CR0: 80050033 CR2: 00000000 CR3: 01a1a000 CR4: 001407f0 Stack: f743ca80 f744a404 ec03fdcc c10e3952 00003aba f743ca80 00000246 f743ca80 00000246 00000000 00000001 00000000 ebd45ff6 ebd458a4 f6d3c500 ebd45578 ebd44b01 ec03fe18 f99e1bc2 00000002 ebd44bc0 f9851770 00000000 f6d3c500 Call Trace: [] ? ring_buffer_unlock_commit+0xa2/0xd0 [] __iterate_interfaces+0x82/0x110 [mac80211] [] ? iwl_mvm_bt_coex_reduced_txp+0x140/0x140 [iwlmvm] [] ieee80211_iterate_active_interfaces_atomic+0x1a/0x20 [mac80211] [] iwl_mvm_bt_coex_notif_handle+0x77/0x280 [iwlmvm] [] iwl_mvm_rx_bt_coex_notif_old+0x211/0x220 [iwlmvm] [] iwl_mvm_rx_bt_coex_notif+0x19b/0x1b0 [iwlmvm] [] iwl_mvm_async_handlers_wk+0x7f/0xe0 [iwlmvm] Fixes: 123f515635b1 ("iwlwifi: mvm: BT Coex - add support for TTC / RRC") Signed-off-by: Emmanuel Grumbach Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/iwlwifi/mvm/coex.c | 3 ++- drivers/net/wireless/iwlwifi/mvm/coex_legacy.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c index a3bfda45d9e6a2..ae5a4ec7556ab0 100644 --- a/drivers/net/wireless/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/iwlwifi/mvm/coex.c @@ -793,7 +793,8 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, if (!vif->bss_conf.assoc) smps_mode = IEEE80211_SMPS_AUTOMATIC; - if (IWL_COEX_IS_RRC_ON(mvm->last_bt_notif.ttc_rrc_status, + if (mvmvif->phy_ctxt && + IWL_COEX_IS_RRC_ON(mvm->last_bt_notif.ttc_rrc_status, mvmvif->phy_ctxt->id)) smps_mode = IEEE80211_SMPS_AUTOMATIC; diff --git a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c b/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c index b3210cfbecc8e4..d8045856995dd1 100644 --- a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c +++ b/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c @@ -832,7 +832,8 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, if (!vif->bss_conf.assoc) smps_mode = IEEE80211_SMPS_AUTOMATIC; - if (data->notif->rrc_enabled & BIT(mvmvif->phy_ctxt->id)) + if (mvmvif->phy_ctxt && + data->notif->rrc_enabled & BIT(mvmvif->phy_ctxt->id)) smps_mode = IEEE80211_SMPS_AUTOMATIC; IWL_DEBUG_COEX(data->mvm, From fa12a956ac8d90fd0403efe68e3111dc7884ceb6 Mon Sep 17 00:00:00 2001 From: Andrei Otcheretianski Date: Sun, 15 Feb 2015 18:33:23 +0200 Subject: [PATCH 528/788] iwlwifi: mvm: Fix ROC removal commit 833d9b9785b3eedfaf2c869a6a63deba88058599 upstream. iwl_mvm_stop_roc removes TE only if running flag is set. This is not correct since this flag is only set when the TE is started. This resulted in a TE not being removed, when mac80211 believes that there are no active ROCs. Fixes: bf5da87f60a9 ("iwlwifi: mvm: add remove flow for AUX ROC time events") Signed-off-by: Andrei Otcheretianski Reviewed-by: Matti Gottlieb Signed-off-by: Emmanuel Grumbach Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/iwlwifi/mvm/time-event.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c index 54fafbf9a711fb..f8d6f306dd76d2 100644 --- a/drivers/net/wireless/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/iwlwifi/mvm/time-event.c @@ -750,8 +750,7 @@ void iwl_mvm_stop_roc(struct iwl_mvm *mvm) * request */ list_for_each_entry(te_data, &mvm->time_event_list, list) { - if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE && - te_data->running) { + if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) { mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif); is_p2p = true; goto remove_te; @@ -766,10 +765,8 @@ void iwl_mvm_stop_roc(struct iwl_mvm *mvm) * request */ list_for_each_entry(te_data, &mvm->aux_roc_te_list, list) { - if (te_data->running) { - mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif); - goto remove_te; - } + mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif); + goto remove_te; } remove_te: From 3a72c7f1bc0d055493ade4ab4d017309268d526c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 16 Mar 2015 15:18:13 +0100 Subject: [PATCH 529/788] uas: Add US_FL_NO_ATA_1X for Initio Corporation controllers / devices commit bda13e35d584dabf52c9f77e0fe62683ac4d9f86 upstream. A new uas compatible controller has shown up in some people's devices from the manufacturer Initio Corporation, this controller needs the US_FL_NO_ATA_1X quirk to work properly with uas, so add it to the uas quirks table. Reported-and-tested-by: Benjamin Tissoires Cc: Benjamin Tissoires Signed-off-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_uas.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h index 82570425fdfe38..c85ea530085f12 100644 --- a/drivers/usb/storage/unusual_uas.h +++ b/drivers/usb/storage/unusual_uas.h @@ -113,6 +113,13 @@ UNUSUAL_DEV(0x0bc2, 0xab2a, 0x0000, 0x9999, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NO_ATA_1X), +/* Reported-by: Benjamin Tissoires */ +UNUSUAL_DEV(0x13fd, 0x3940, 0x0000, 0x9999, + "Initio Corporation", + "", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_ATA_1X), + /* Reported-by: Tom Arild Naess */ UNUSUAL_DEV(0x152d, 0x0539, 0x0000, 0x9999, "JMicron", From 2904a33c4b08922cdae642a528ef4df2487b56da Mon Sep 17 00:00:00 2001 From: David Dueck Date: Sun, 8 Feb 2015 16:29:30 +0100 Subject: [PATCH 530/788] usb: phy: am335x-control: check return value of bus_find_device commit d0f347d62814ec0f599a05c61c5619d5e999e4ae upstream. This fixes a potential null pointer dereference. Fixes: d4332013919a ("driver core: dev_get_drvdata: Don't check for NULL dev") Acked-by: Sebastian Andrzej Siewior Signed-off-by: David Dueck Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-am335x-control.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/phy/phy-am335x-control.c b/drivers/usb/phy/phy-am335x-control.c index 403fab77272482..7b3035ff94347a 100644 --- a/drivers/usb/phy/phy-am335x-control.c +++ b/drivers/usb/phy/phy-am335x-control.c @@ -126,6 +126,9 @@ struct phy_control *am335x_get_phy_control(struct device *dev) return NULL; dev = bus_find_device(&platform_bus_type, NULL, node, match); + if (!dev) + return NULL; + ctrl_usb = dev_get_drvdata(dev); if (!ctrl_usb) return NULL; From f232e3d094d55315cd9801db669850068e0cfc06 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Sun, 8 Mar 2015 16:05:01 +0800 Subject: [PATCH 531/788] usb: chipidea: otg: add a_alt_hnp_support response for B device commit d20f7807996c69537e07443ef8dec4e01a28b099 upstream. This patch adds response to a_alt_hnp_support set feature request from legacy A device, that is, B-device can provide a message to the user indicating that the user needs to connect the B-device to an alternate port on the A-device. A device sets this feature indicates to the B-device that it is connected to an A-device port that is not capable of HNP, but that the A-device does have an alternate port that is capable of HNP. [Peter] Without this patch, the OTG B device can't be enumerated on non-HNP port at A device, see below log: [ 2.287464] usb 1-1: Dual-Role OTG device on non-HNP port [ 2.293105] usb 1-1: can't set HNP mode: -32 [ 2.417422] usb 1-1: new high-speed USB device number 4 using ci_hdrc [ 2.460635] usb 1-1: Dual-Role OTG device on non-HNP port [ 2.466424] usb 1-1: can't set HNP mode: -32 [ 2.587464] usb 1-1: new high-speed USB device number 5 using ci_hdrc [ 2.630649] usb 1-1: Dual-Role OTG device on non-HNP port [ 2.636436] usb 1-1: can't set HNP mode: -32 [ 2.641003] usb usb1-port1: unable to enumerate USB device Acked-by: Peter Chen Signed-off-by: Li Jun Signed-off-by: Peter Chen Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/udc.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 4fe18ce3bd5aa6..7c145655cd4b56 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -929,6 +929,13 @@ __acquires(hwep->lock) return retval; } +static int otg_a_alt_hnp_support(struct ci_hdrc *ci) +{ + dev_warn(&ci->gadget.dev, + "connect the device to an alternate port if you want HNP\n"); + return isr_setup_status_phase(ci); +} + /** * isr_setup_packet_handler: setup packet handler * @ci: UDC descriptor @@ -1061,6 +1068,10 @@ __acquires(ci->lock) ci); } break; + case USB_DEVICE_A_ALT_HNP_SUPPORT: + if (ci_otg_is_fsm_mode(ci)) + err = otg_a_alt_hnp_support(ci); + break; default: goto delegate; } From 75d10b6098a419dca452c86ee6b2f4117006f826 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Thu, 12 Mar 2015 09:47:53 +0800 Subject: [PATCH 532/788] usb: common: otg-fsm: only signal connect after switching to peripheral commit a886bd92267c9e3d5c912860c6fb5a68479a7643 upstream. We should signal connect (pull up dp) after we have already at peripheral mode, otherwise, the dp may be toggled due to we reset controller or do disconnect during the initialization for peripheral, then, the host may be confused during the enumeration, eg, it finds the reset can't succeed, but the device is still there, see below error message. hub 1-0:1.0: USB hub found hub 1-0:1.0: 1 port detected hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: Cannot enable port 1. Maybe the USB cable is bad? hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: Cannot enable port 1. Maybe the USB cable is bad? hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: Cannot enable port 1. Maybe the USB cable is bad? hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: Cannot enable port 1. Maybe the USB cable is bad? hub 1-0:1.0: unable to enumerate USB device on port 1 Fixes: the issue existed when the otg fsm code was added. Signed-off-by: Peter Chen Signed-off-by: Greg Kroah-Hartman --- drivers/usb/common/usb-otg-fsm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/common/usb-otg-fsm.c b/drivers/usb/common/usb-otg-fsm.c index c6b35b77dab73a..61d538aa23466b 100644 --- a/drivers/usb/common/usb-otg-fsm.c +++ b/drivers/usb/common/usb-otg-fsm.c @@ -150,9 +150,9 @@ static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) break; case OTG_STATE_B_PERIPHERAL: otg_chrg_vbus(fsm, 0); - otg_loc_conn(fsm, 1); otg_loc_sof(fsm, 0); otg_set_protocol(fsm, PROTO_GADGET); + otg_loc_conn(fsm, 1); break; case OTG_STATE_B_WAIT_ACON: otg_chrg_vbus(fsm, 0); @@ -213,10 +213,10 @@ static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) break; case OTG_STATE_A_PERIPHERAL: - otg_loc_conn(fsm, 1); otg_loc_sof(fsm, 0); otg_set_protocol(fsm, PROTO_GADGET); otg_drv_vbus(fsm, 1); + otg_loc_conn(fsm, 1); otg_add_timer(fsm, A_BIDL_ADIS); break; case OTG_STATE_A_WAIT_VFALL: From 78dc9937928e17b285d66d5173aceff46fd69e0b Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 25 Feb 2015 16:16:29 +0100 Subject: [PATCH 533/788] phy: Find the right match in devm_phy_destroy() commit 2f1bce487cd0a02623cff3d877940f9a2026341c upstream. devm_phy_create() stores the pointer to the new PHY at the address returned by devres_alloc(). The res parameter passed to devm_phy_match() is therefore the location where the pointer to the PHY is stored, hence it needs to be dereferenced before comparing to the match data in order to find the correct match. Signed-off-by: Thierry Reding Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Greg Kroah-Hartman --- drivers/phy/phy-core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index a12d35338313bd..04fc84f2b28990 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -52,7 +52,9 @@ static void devm_phy_consume(struct device *dev, void *res) static int devm_phy_match(struct device *dev, void *res, void *match_data) { - return res == match_data; + struct phy **phy = res; + + return *phy == match_data; } /** From 200f5a5c688f410b599805ff0f08c54aeffd3156 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 24 Feb 2015 09:23:01 -0600 Subject: [PATCH 534/788] rtlwifi: Improve handling of IPv6 packets commit c8f0345586694a33f828bc6b177fb21eb1702325 upstream. Routine rtl_is_special_data() is supposed to identify packets that need to use a low bit rate so that the probability of successful transmission is high. The current version has a bug that causes all IPv6 packets to be labelled as special, with a corresponding low rate of transmission. A complete fix will be quite intrusive, but until that is available, all IPv6 packets are identified as regular. This patch also removes a magic number. Reported-and-tested-by: Alan Fisher Signed-off-by: Larry Finger Cc: Alan Fisher Signed-off-by: Kalle Valo Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/rtlwifi/base.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/rtlwifi/base.c b/drivers/net/wireless/rtlwifi/base.c index 40b6d1d006d7ab..af2486965782d4 100644 --- a/drivers/net/wireless/rtlwifi/base.c +++ b/drivers/net/wireless/rtlwifi/base.c @@ -1314,8 +1314,11 @@ u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx) } return true; - } else if (0x86DD == ether_type) { - return true; + } else if (ETH_P_IPV6 == ether_type) { + /* TODO: Handle any IPv6 cases that need special handling. + * For now, always return false + */ + goto end; } end: From 9ce9d3fb7754af5f731499ec87b842739db4d03a Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Thu, 26 Feb 2015 18:20:48 +0100 Subject: [PATCH 535/788] cpuidle: mvebu: Fix the CPU PM notifier usage commit 43b68879de27b1993518687fbc6013da80cdcbfe upstream. As stated in kernel/cpu_pm.c, "Platform is responsible for ensuring that cpu_pm_enter is not called twice on the same CPU before cpu_pm_exit is called.". In the current code in case of failure when calling mvebu_v7_cpu_suspend, the function cpu_pm_exit() is never called whereas cpu_pm_enter() was called just before. This patch moves the cpu_pm_exit() in order to balance the cpu_pm_enter() calls. Reported-by: Fulvio Benini Signed-off-by: Gregory CLEMENT Signed-off-by: Daniel Lezcano Signed-off-by: Greg Kroah-Hartman --- drivers/cpuidle/cpuidle-mvebu-v7.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/cpuidle/cpuidle-mvebu-v7.c b/drivers/cpuidle/cpuidle-mvebu-v7.c index 38e68618513a47..cefa07438ae1b5 100644 --- a/drivers/cpuidle/cpuidle-mvebu-v7.c +++ b/drivers/cpuidle/cpuidle-mvebu-v7.c @@ -37,11 +37,11 @@ static int mvebu_v7_enter_idle(struct cpuidle_device *dev, deepidle = true; ret = mvebu_v7_cpu_suspend(deepidle); + cpu_pm_exit(); + if (ret) return ret; - cpu_pm_exit(); - return index; } From bae96235997e41843eeb118bfb12aa4365266ce2 Mon Sep 17 00:00:00 2001 From: Pontus Fuchs Date: Fri, 6 Mar 2015 16:18:41 +0100 Subject: [PATCH 536/788] brcmfmac: Perform bound checking on vendor command buffer commit 3f1615340acea54e21f4b9d4d65921540dca84b2 upstream. A short or malformed vendor command buffer could cause reads outside the command buffer. Signed-off-by: Pontus Fuchs [arend@broadcom.com: slightly modified debug trace output] Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/brcm80211/brcmfmac/vendor.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c index 50cdf7090198b3..8eff2753abadeb 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c @@ -39,13 +39,22 @@ static int brcmf_cfg80211_vndr_cmds_dcmd_handler(struct wiphy *wiphy, void *dcmd_buf = NULL, *wr_pointer; u16 msglen, maxmsglen = PAGE_SIZE - 0x100; - brcmf_dbg(TRACE, "cmd %x set %d len %d\n", cmdhdr->cmd, cmdhdr->set, - cmdhdr->len); + if (len < sizeof(*cmdhdr)) { + brcmf_err("vendor command too short: %d\n", len); + return -EINVAL; + } vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); ifp = vif->ifp; - len -= sizeof(struct brcmf_vndr_dcmd_hdr); + brcmf_dbg(TRACE, "ifidx=%d, cmd=%d\n", ifp->ifidx, cmdhdr->cmd); + + if (cmdhdr->offset > len) { + brcmf_err("bad buffer offset %d > %d\n", cmdhdr->offset, len); + return -EINVAL; + } + + len -= cmdhdr->offset; ret_len = cmdhdr->len; if (ret_len > 0 || len > 0) { if (len > BRCMF_DCMD_MAXLEN) { From 874fee53430ab4fd92326832a99402d00e1f629c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 18 Mar 2015 00:21:32 +0200 Subject: [PATCH 537/788] of/irq: Fix of_irq_parse_one() returned error codes commit d7c146053dd195b90c79b9b8131431f44541d015 upstream. The error code paths that require cleanup use a goto to jump to the cleanup code and return an error code. However, the error code variable res, which is initialized to -EINVAL when declared, is then overwritten with the return value of of_parse_phandle_with_args(), and reused as the return code from of_irq_parse_one(). This leads to an undetermined error being returned instead of the expected -EINVAL value. Fix it. Signed-off-by: Laurent Pinchart Signed-off-by: Rob Herring Signed-off-by: Greg Kroah-Hartman --- drivers/of/irq.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 0d7765807f4940..1a7980692f254c 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -290,7 +290,7 @@ int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_ar struct device_node *p; const __be32 *intspec, *tmp, *addr; u32 intsize, intlen; - int i, res = -EINVAL; + int i, res; pr_debug("of_irq_parse_one: dev=%s, index=%d\n", of_node_full_name(device), index); @@ -323,15 +323,19 @@ int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_ar /* Get size of interrupt specifier */ tmp = of_get_property(p, "#interrupt-cells", NULL); - if (tmp == NULL) + if (tmp == NULL) { + res = -EINVAL; goto out; + } intsize = be32_to_cpu(*tmp); pr_debug(" intsize=%d intlen=%d\n", intsize, intlen); /* Check index */ - if ((index + 1) * intsize > intlen) + if ((index + 1) * intsize > intlen) { + res = -EINVAL; goto out; + } /* Copy intspec into irq structure */ intspec += index * intsize; From 20a517d114bc76e850ad4e8e9d5c002df2ab7c4d Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 19 Feb 2015 18:03:11 +0100 Subject: [PATCH 538/788] perf: Fix irq_work 'tail' recursion commit d525211f9d1be8b523ec7633f080f2116f5ea536 upstream. Vince reported a watchdog lockup like: [] perf_tp_event+0xc4/0x210 [] perf_trace_lock+0x12a/0x160 [] lock_release+0x130/0x260 [] _raw_spin_unlock_irqrestore+0x24/0x40 [] do_send_sig_info+0x5d/0x80 [] send_sigio_to_task+0x12f/0x1a0 [] send_sigio+0xae/0x100 [] kill_fasync+0x97/0xf0 [] perf_event_wakeup+0xd4/0xf0 [] perf_pending_event+0x33/0x60 [] irq_work_run_list+0x4c/0x80 [] irq_work_run+0x18/0x40 [] smp_trace_irq_work_interrupt+0x3f/0xc0 [] trace_irq_work_interrupt+0x6d/0x80 Which is caused by an irq_work generating new irq_work and therefore not allowing forward progress. This happens because processing the perf irq_work triggers another perf event (tracepoint stuff) which in turn generates an irq_work ad infinitum. Avoid this by raising the recursion counter in the irq_work -- which effectively disables all software events (including tracepoints) from actually triggering again. Reported-by: Vince Weaver Tested-by: Vince Weaver Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Paul Mackerras Cc: Steven Rostedt Link: http://lkml.kernel.org/r/20150219170311.GH21418@twins.programming.kicks-ass.net Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- kernel/events/core.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/kernel/events/core.c b/kernel/events/core.c index 19efcf13375a29..795962442f99fc 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -4412,6 +4412,13 @@ static void perf_pending_event(struct irq_work *entry) { struct perf_event *event = container_of(entry, struct perf_event, pending); + int rctx; + + rctx = perf_swevent_get_recursion_context(); + /* + * If we 'fail' here, that's OK, it means recursion is already disabled + * and we won't recurse 'further'. + */ if (event->pending_disable) { event->pending_disable = 0; @@ -4422,6 +4429,9 @@ static void perf_pending_event(struct irq_work *entry) event->pending_wakeup = 0; perf_event_wakeup(event); } + + if (rctx >= 0) + perf_swevent_put_recursion_context(rctx); } /* From ab4eba1a775b8a1eaf94ffe1abd2d588943460df Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Sat, 7 Mar 2015 16:36:37 +0000 Subject: [PATCH 539/788] staging: vt6656: vnt_rf_setpower: fix missing rate RATE_12M commit 163fe301b9f78b6de57d0014eafe504fd20c0cd4 upstream. When the driver sets this rate a power of zero value is set causing data flow stoppage until another rate is tried. Signed-off-by: Malcolm Priestley Signed-off-by: Greg Kroah-Hartman --- drivers/staging/vt6656/rf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/vt6656/rf.c b/drivers/staging/vt6656/rf.c index c42cde59f598ef..c4286ccac32034 100644 --- a/drivers/staging/vt6656/rf.c +++ b/drivers/staging/vt6656/rf.c @@ -640,6 +640,7 @@ int vnt_rf_setpower(struct vnt_private *priv, u32 rate, u32 channel) break; case RATE_6M: case RATE_9M: + case RATE_12M: case RATE_18M: case RATE_24M: case RATE_36M: From cb5e2a71141b7527472d7727ef4c76c90018ebe7 Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Sat, 7 Mar 2015 17:04:54 +0000 Subject: [PATCH 540/788] vt6655: RFbSetPower fix missing rate RATE_12M commit 40c8790bcb7ac74f3038153cd09310e220c6a1df upstream. When the driver sets this rate a power of zero value is set causing data flow stoppage until another rate is tried. Signed-off-by: Malcolm Priestley Signed-off-by: Greg Kroah-Hartman --- drivers/staging/vt6655/rf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/vt6655/rf.c b/drivers/staging/vt6655/rf.c index 32ef99341e204f..5d65ad09bcd157 100644 --- a/drivers/staging/vt6655/rf.c +++ b/drivers/staging/vt6655/rf.c @@ -791,6 +791,7 @@ bool RFbSetPower( break; case RATE_6M: case RATE_9M: + case RATE_12M: case RATE_18M: byPwr = priv->abyOFDMPwrTbl[uCH]; if (priv->byRFType == RF_UW2452) From 99aadf9b8e6344eba5e841446fc16370636e0f40 Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Sat, 7 Mar 2015 17:04:55 +0000 Subject: [PATCH 541/788] vt6655: Fix late setting of byRFType. commit 1f51d5801859e0b382dcc8f06875811d63ec8953 upstream. byRFType is not set prior to registration of mac80211 causing unpredictable operation after channel scans. With byRFType unset all channels are enabled this causes tx power to be set to values not present its eeprom. Move setting of this variable to vt6655_probe. byRFType must have a mask set. byRevId not used by driver and is removed. Signed-off-by: Malcolm Priestley Signed-off-by: Greg Kroah-Hartman --- drivers/staging/vt6655/device_main.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/staging/vt6655/device_main.c b/drivers/staging/vt6655/device_main.c index cd1a277d853b5d..ace0521f11519f 100644 --- a/drivers/staging/vt6655/device_main.c +++ b/drivers/staging/vt6655/device_main.c @@ -357,16 +357,6 @@ static void device_init_registers(struct vnt_private *pDevice) /* zonetype initial */ pDevice->byOriginalZonetype = pDevice->abyEEPROM[EEP_OFS_ZONETYPE]; - /* Get RFType */ - pDevice->byRFType = SROMbyReadEmbedded(pDevice->PortOffset, EEP_OFS_RFTYPE); - - /* force change RevID for VT3253 emu */ - if ((pDevice->byRFType & RF_EMU) != 0) - pDevice->byRevId = 0x80; - - pDevice->byRFType &= RF_MASK; - pr_debug("pDevice->byRFType = %x\n", pDevice->byRFType); - if (!pDevice->bZoneRegExist) pDevice->byZoneType = pDevice->abyEEPROM[EEP_OFS_ZONETYPE]; @@ -1806,6 +1796,12 @@ vt6655_probe(struct pci_dev *pcid, const struct pci_device_id *ent) MACvInitialize(priv->PortOffset); MACvReadEtherAddress(priv->PortOffset, priv->abyCurrentNetAddr); + /* Get RFType */ + priv->byRFType = SROMbyReadEmbedded(priv->PortOffset, EEP_OFS_RFTYPE); + priv->byRFType &= RF_MASK; + + dev_dbg(&pcid->dev, "RF Type = %x\n", priv->byRFType); + device_get_options(priv); device_set_options(priv); /* Mask out the options cannot be set to the chip */ From fd55246133075d717f1466a32bb3e295d056a454 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 9 Mar 2015 12:16:42 +0200 Subject: [PATCH 542/788] dmaengine: dw: append MODULE_ALIAS for platform driver commit a104a45ba7a51b5b4c5e8437020d9d48edf22f89 upstream. The commit 9cade1a46c77 (dma: dw: split driver to library part and platform code) introduced a separate platform driver but missed to add a MODULE_ALIAS("platform:dw_dmac"); to that module. The patch adds this to get driver loaded automatically if platform device is registered. Reported-by: "Blin, Jerome" Fixes: 9cade1a46c77 (dma: dw: split driver to library part and platform code) Signed-off-by: Andy Shevchenko Signed-off-by: Vinod Koul Signed-off-by: Greg Kroah-Hartman --- drivers/dma/dw/platform.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/dma/dw/platform.c b/drivers/dma/dw/platform.c index 32ea1aca7a0ea2..272f01fe667b52 100644 --- a/drivers/dma/dw/platform.c +++ b/drivers/dma/dw/platform.c @@ -26,6 +26,8 @@ #include "internal.h" +#define DRV_NAME "dw_dmac" + static struct dma_chan *dw_dma_of_xlate(struct of_phandle_args *dma_spec, struct of_dma *ofdma) { @@ -284,7 +286,7 @@ static struct platform_driver dw_driver = { .remove = dw_remove, .shutdown = dw_shutdown, .driver = { - .name = "dw_dmac", + .name = DRV_NAME, .pm = &dw_dev_pm_ops, .of_match_table = of_match_ptr(dw_dma_of_id_table), .acpi_match_table = ACPI_PTR(dw_dma_acpi_id_table), @@ -305,3 +307,4 @@ module_exit(dw_exit); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller platform driver"); +MODULE_ALIAS("platform:" DRV_NAME); From 72267c2e77226ebfca62d8dbb9bb8fca061bd914 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Fri, 27 Feb 2015 14:04:27 -0500 Subject: [PATCH 543/788] dm: hold suspend_lock while suspending device during device deletion commit ab7c7bb6f4ab95dbca96fcfc4463cd69843e3e24 upstream. __dm_destroy() must take the suspend_lock so that its presuspend and postsuspend calls do not race with an internal suspend. Signed-off-by: Mikulas Patocka Signed-off-by: Mike Snitzer Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 64b10e006f9c63..1fb4ee45a0adf1 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -2526,10 +2526,16 @@ static void __dm_destroy(struct mapped_device *md, bool wait) set_bit(DMF_FREEING, &md->flags); spin_unlock(&_minor_lock); + /* + * Take suspend_lock so that presuspend and postsuspend methods + * do not race with internal suspend. + */ + mutex_lock(&md->suspend_lock); if (!dm_suspended_md(md)) { dm_table_presuspend_targets(map); dm_table_postsuspend_targets(map); } + mutex_unlock(&md->suspend_lock); /* dm_put_live_table must be before msleep, otherwise deadlock is possible */ dm_put_live_table(md, srcu_idx); From f067d93f11803f587ec2947ba8ac2e4c5f7e3fd9 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Fri, 27 Feb 2015 10:44:38 -0800 Subject: [PATCH 544/788] dm io: deal with wandering queue limits when handling REQ_DISCARD and REQ_WRITE_SAME commit e5db29806b99ce2b2640d2e4d4fcb983cea115c5 upstream. Since it's possible for the discard and write same queue limits to change while the upper level command is being sliced and diced, fix up both of them (a) to reject IO if the special command is unsupported at the start of the function and (b) read the limits once and let the commands error out on their own if the status happens to change. Signed-off-by: Darrick J. Wong Signed-off-by: Mikulas Patocka Signed-off-by: Mike Snitzer Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-io.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/md/dm-io.c b/drivers/md/dm-io.c index 37de0173b6d232..74adcd2c967ec8 100644 --- a/drivers/md/dm-io.c +++ b/drivers/md/dm-io.c @@ -289,9 +289,16 @@ static void do_region(int rw, unsigned region, struct dm_io_region *where, struct request_queue *q = bdev_get_queue(where->bdev); unsigned short logical_block_size = queue_logical_block_size(q); sector_t num_sectors; + unsigned int uninitialized_var(special_cmd_max_sectors); - /* Reject unsupported discard requests */ - if ((rw & REQ_DISCARD) && !blk_queue_discard(q)) { + /* + * Reject unsupported discard and write same requests. + */ + if (rw & REQ_DISCARD) + special_cmd_max_sectors = q->limits.max_discard_sectors; + else if (rw & REQ_WRITE_SAME) + special_cmd_max_sectors = q->limits.max_write_same_sectors; + if ((rw & (REQ_DISCARD | REQ_WRITE_SAME)) && special_cmd_max_sectors == 0) { dec_count(io, region, -EOPNOTSUPP); return; } @@ -317,7 +324,7 @@ static void do_region(int rw, unsigned region, struct dm_io_region *where, store_io_and_region_in_bio(bio, io, region); if (rw & REQ_DISCARD) { - num_sectors = min_t(sector_t, q->limits.max_discard_sectors, remaining); + num_sectors = min_t(sector_t, special_cmd_max_sectors, remaining); bio->bi_iter.bi_size = num_sectors << SECTOR_SHIFT; remaining -= num_sectors; } else if (rw & REQ_WRITE_SAME) { @@ -326,7 +333,7 @@ static void do_region(int rw, unsigned region, struct dm_io_region *where, */ dp->get_page(dp, &page, &len, &offset); bio_add_page(bio, page, logical_block_size, offset); - num_sectors = min_t(sector_t, q->limits.max_write_same_sectors, remaining); + num_sectors = min_t(sector_t, special_cmd_max_sectors, remaining); bio->bi_iter.bi_size = num_sectors << SECTOR_SHIFT; offset = 0; From b6bf8494e4fb260bf8738a1d1c9434df4996eedf Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Fri, 27 Feb 2015 14:09:12 +0000 Subject: [PATCH 545/788] dm thin: fix to consistently zero-fill reads to unprovisioned blocks commit 5f027a3bf184d1d36e68745f7cd3718a8b879cc0 upstream. It was always intended that a read to an unprovisioned block will return zeroes regardless of whether the pool is in read-only or read-write mode. thin_bio_map() was inconsistent with its handling of such reads when the pool is in read-only mode, it now properly zero-fills the bios it returns in response to unprovisioned block reads. Eliminate thin_bio_map()'s special read-only mode handling of -ENODATA and just allow the IO to be deferred to the worker which will result in pool->process_bio() handling the IO (which already properly zero-fills reads to unprovisioned blocks). Reported-by: Eric Sandeen Signed-off-by: Joe Thornber Signed-off-by: Mike Snitzer Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-thin.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c index 07705ee181e3d2..159a113c3ad8ab 100644 --- a/drivers/md/dm-thin.c +++ b/drivers/md/dm-thin.c @@ -2357,17 +2357,6 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio) return DM_MAPIO_REMAPPED; case -ENODATA: - if (get_pool_mode(tc->pool) == PM_READ_ONLY) { - /* - * This block isn't provisioned, and we have no way - * of doing so. - */ - handle_unserviceable_bio(tc->pool, bio); - cell_defer_no_holder(tc, virt_cell); - return DM_MAPIO_SUBMITTED; - } - /* fall through */ - case -EWOULDBLOCK: thin_defer_cell(tc, virt_cell); return DM_MAPIO_SUBMITTED; From 3d9f57bb1c694d1cf6ba998cb960ed045b02726e Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 26 Feb 2015 11:40:35 -0500 Subject: [PATCH 546/788] dm snapshot: suspend origin when doing exception handover commit b735fede8d957d9d255e9c5cf3964cfa59799637 upstream. In the function snapshot_resume we perform exception store handover. If there is another active snapshot target, the exception store is moved from this target to the target that is being resumed. The problem is that if there is some pending exception, it will point to an incorrect exception store after that handover, causing a crash due to dm-snap-persistent.c:get_exception()'s BUG_ON. This bug can be triggered by repeatedly changing snapshot permissions with "lvchange -p r" and "lvchange -p rw" while there are writes on the associated origin device. To fix this bug, we must suspend the origin device when doing the exception store handover to make sure that there are no pending exceptions: - introduce _origin_hash that keeps track of dm_origin structures. - introduce functions __lookup_dm_origin, __insert_dm_origin and __remove_dm_origin that manipulate the origin hash. - modify snapshot_resume so that it calls dm_internal_suspend_fast() and dm_internal_resume_fast() on the origin device. NOTE to stable@ people: When backporting to kernels 3.12-3.18, use dm_internal_suspend and dm_internal_resume instead of dm_internal_suspend_fast and dm_internal_resume_fast. When backporting to kernels older than 3.12, you need to pick functions dm_internal_suspend and dm_internal_resume from the commit fd2ed4d252701d3bbed4cd3e3d267ad469bb832a. Signed-off-by: Mikulas Patocka Signed-off-by: Mike Snitzer Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-snap.c | 93 +++++++++++++++++++++++++++++++++++++++----- drivers/md/dm.c | 2 + 2 files changed, 86 insertions(+), 9 deletions(-) diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index 8b204ae216ab62..c2bf822bad6f69 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c @@ -20,6 +20,8 @@ #include #include +#include "dm.h" + #include "dm-exception-store.h" #define DM_MSG_PREFIX "snapshots" @@ -290,6 +292,16 @@ struct origin { struct list_head snapshots; }; +/* + * This structure is allocated for each origin target + */ +struct dm_origin { + struct dm_dev *dev; + struct dm_target *ti; + unsigned split_boundary; + struct list_head hash_list; +}; + /* * Size of the hash table for origin volumes. If we make this * the size of the minors list then it should be nearly perfect @@ -297,6 +309,7 @@ struct origin { #define ORIGIN_HASH_SIZE 256 #define ORIGIN_MASK 0xFF static struct list_head *_origins; +static struct list_head *_dm_origins; static struct rw_semaphore _origins_lock; static DECLARE_WAIT_QUEUE_HEAD(_pending_exceptions_done); @@ -310,12 +323,22 @@ static int init_origin_hash(void) _origins = kmalloc(ORIGIN_HASH_SIZE * sizeof(struct list_head), GFP_KERNEL); if (!_origins) { - DMERR("unable to allocate memory"); + DMERR("unable to allocate memory for _origins"); return -ENOMEM; } - for (i = 0; i < ORIGIN_HASH_SIZE; i++) INIT_LIST_HEAD(_origins + i); + + _dm_origins = kmalloc(ORIGIN_HASH_SIZE * sizeof(struct list_head), + GFP_KERNEL); + if (!_dm_origins) { + DMERR("unable to allocate memory for _dm_origins"); + kfree(_origins); + return -ENOMEM; + } + for (i = 0; i < ORIGIN_HASH_SIZE; i++) + INIT_LIST_HEAD(_dm_origins + i); + init_rwsem(&_origins_lock); return 0; @@ -324,6 +347,7 @@ static int init_origin_hash(void) static void exit_origin_hash(void) { kfree(_origins); + kfree(_dm_origins); } static unsigned origin_hash(struct block_device *bdev) @@ -350,6 +374,30 @@ static void __insert_origin(struct origin *o) list_add_tail(&o->hash_list, sl); } +static struct dm_origin *__lookup_dm_origin(struct block_device *origin) +{ + struct list_head *ol; + struct dm_origin *o; + + ol = &_dm_origins[origin_hash(origin)]; + list_for_each_entry (o, ol, hash_list) + if (bdev_equal(o->dev->bdev, origin)) + return o; + + return NULL; +} + +static void __insert_dm_origin(struct dm_origin *o) +{ + struct list_head *sl = &_dm_origins[origin_hash(o->dev->bdev)]; + list_add_tail(&o->hash_list, sl); +} + +static void __remove_dm_origin(struct dm_origin *o) +{ + list_del(&o->hash_list); +} + /* * _origins_lock must be held when calling this function. * Returns number of snapshots registered using the supplied cow device, plus: @@ -1841,8 +1889,20 @@ static void snapshot_resume(struct dm_target *ti) { struct dm_snapshot *s = ti->private; struct dm_snapshot *snap_src = NULL, *snap_dest = NULL; + struct dm_origin *o; + struct mapped_device *origin_md = NULL; down_read(&_origins_lock); + + o = __lookup_dm_origin(s->origin->bdev); + if (o) + origin_md = dm_table_get_md(o->ti->table); + if (origin_md == dm_table_get_md(ti->table)) + origin_md = NULL; + + if (origin_md) + dm_internal_suspend_fast(origin_md); + (void) __find_snapshots_sharing_cow(s, &snap_src, &snap_dest, NULL); if (snap_src && snap_dest) { down_write(&snap_src->lock); @@ -1851,6 +1911,10 @@ static void snapshot_resume(struct dm_target *ti) up_write(&snap_dest->lock); up_write(&snap_src->lock); } + + if (origin_md) + dm_internal_resume_fast(origin_md); + up_read(&_origins_lock); /* Now we have correct chunk size, reregister */ @@ -2133,11 +2197,6 @@ static int origin_write_extent(struct dm_snapshot *merging_snap, * Origin: maps a linear range of a device, with hooks for snapshotting. */ -struct dm_origin { - struct dm_dev *dev; - unsigned split_boundary; -}; - /* * Construct an origin mapping: * The context for an origin is merely a 'struct dm_dev *' @@ -2166,6 +2225,7 @@ static int origin_ctr(struct dm_target *ti, unsigned int argc, char **argv) goto bad_open; } + o->ti = ti; ti->private = o; ti->num_flush_bios = 1; @@ -2180,6 +2240,7 @@ static int origin_ctr(struct dm_target *ti, unsigned int argc, char **argv) static void origin_dtr(struct dm_target *ti) { struct dm_origin *o = ti->private; + dm_put_device(ti, o->dev); kfree(o); } @@ -2216,6 +2277,19 @@ static void origin_resume(struct dm_target *ti) struct dm_origin *o = ti->private; o->split_boundary = get_origin_minimum_chunksize(o->dev->bdev); + + down_write(&_origins_lock); + __insert_dm_origin(o); + up_write(&_origins_lock); +} + +static void origin_postsuspend(struct dm_target *ti) +{ + struct dm_origin *o = ti->private; + + down_write(&_origins_lock); + __remove_dm_origin(o); + up_write(&_origins_lock); } static void origin_status(struct dm_target *ti, status_type_t type, @@ -2258,12 +2332,13 @@ static int origin_iterate_devices(struct dm_target *ti, static struct target_type origin_target = { .name = "snapshot-origin", - .version = {1, 8, 1}, + .version = {1, 9, 0}, .module = THIS_MODULE, .ctr = origin_ctr, .dtr = origin_dtr, .map = origin_map, .resume = origin_resume, + .postsuspend = origin_postsuspend, .status = origin_status, .merge = origin_merge, .iterate_devices = origin_iterate_devices, @@ -2271,7 +2346,7 @@ static struct target_type origin_target = { static struct target_type snapshot_target = { .name = "snapshot", - .version = {1, 12, 0}, + .version = {1, 13, 0}, .module = THIS_MODULE, .ctr = snapshot_ctr, .dtr = snapshot_dtr, diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 1fb4ee45a0adf1..2d1be922aaead6 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -3007,6 +3007,7 @@ void dm_internal_suspend_fast(struct mapped_device *md) flush_workqueue(md->wq); dm_wait_for_completion(md, TASK_UNINTERRUPTIBLE); } +EXPORT_SYMBOL_GPL(dm_internal_suspend_fast); void dm_internal_resume_fast(struct mapped_device *md) { @@ -3018,6 +3019,7 @@ void dm_internal_resume_fast(struct mapped_device *md) done: mutex_unlock(&md->suspend_lock); } +EXPORT_SYMBOL_GPL(dm_internal_resume_fast); /*----------------------------------------------------------------- * Event notification. From 13027463097742b5901b1fb4a0a458a2f6dc467d Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 26 Feb 2015 11:41:28 -0500 Subject: [PATCH 547/788] dm snapshot: suspend merging snapshot when doing exception handover commit 09ee96b21456883e108c3b00597bb37ec512151b upstream. The "dm snapshot: suspend origin when doing exception handover" commit fixed a exception store handover bug associated with pending exceptions to the "snapshot-origin" target. However, a similar problem exists in snapshot merging. When snapshot merging is in progress, we use the target "snapshot-merge" instead of "snapshot-origin". Consequently, during exception store handover, we must find the snapshot-merge target and suspend its associated mapped_device. To avoid lockdep warnings, the target must be suspended and resumed without holding _origins_lock. Introduce a dm_hold() function that grabs a reference on a mapped_device, but unlike dm_get(), it doesn't crash if the device has the DMF_FREEING flag set, it returns an error in this case. In snapshot_resume() we grab the reference to the origin device using dm_hold() while holding _origins_lock (_origins_lock guarantees that the device won't disappear). Then we release _origins_lock, suspend the device and grab _origins_lock again. NOTE to stable@ people: When backporting to kernels 3.18 and older, use dm_internal_suspend and dm_internal_resume instead of dm_internal_suspend_fast and dm_internal_resume_fast. Signed-off-by: Mikulas Patocka Signed-off-by: Mike Snitzer Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-snap.c | 35 +++++++++++++++++++++++++++++------ drivers/md/dm.c | 13 +++++++++++++ include/linux/device-mapper.h | 1 + 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index c2bf822bad6f69..f83a0f3fc36566 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c @@ -1888,20 +1888,39 @@ static int snapshot_preresume(struct dm_target *ti) static void snapshot_resume(struct dm_target *ti) { struct dm_snapshot *s = ti->private; - struct dm_snapshot *snap_src = NULL, *snap_dest = NULL; + struct dm_snapshot *snap_src = NULL, *snap_dest = NULL, *snap_merging = NULL; struct dm_origin *o; struct mapped_device *origin_md = NULL; + bool must_restart_merging = false; down_read(&_origins_lock); o = __lookup_dm_origin(s->origin->bdev); if (o) origin_md = dm_table_get_md(o->ti->table); + if (!origin_md) { + (void) __find_snapshots_sharing_cow(s, NULL, NULL, &snap_merging); + if (snap_merging) + origin_md = dm_table_get_md(snap_merging->ti->table); + } if (origin_md == dm_table_get_md(ti->table)) origin_md = NULL; + if (origin_md) { + if (dm_hold(origin_md)) + origin_md = NULL; + } - if (origin_md) + up_read(&_origins_lock); + + if (origin_md) { dm_internal_suspend_fast(origin_md); + if (snap_merging && test_bit(RUNNING_MERGE, &snap_merging->state_bits)) { + must_restart_merging = true; + stop_merge(snap_merging); + } + } + + down_read(&_origins_lock); (void) __find_snapshots_sharing_cow(s, &snap_src, &snap_dest, NULL); if (snap_src && snap_dest) { @@ -1912,11 +1931,15 @@ static void snapshot_resume(struct dm_target *ti) up_write(&snap_src->lock); } - if (origin_md) - dm_internal_resume_fast(origin_md); - up_read(&_origins_lock); + if (origin_md) { + if (must_restart_merging) + start_merge(snap_merging); + dm_internal_resume_fast(origin_md); + dm_put(origin_md); + } + /* Now we have correct chunk size, reregister */ reregister_snapshot(s); @@ -2360,7 +2383,7 @@ static struct target_type snapshot_target = { static struct target_type merge_target = { .name = dm_snapshot_merge_target_name, - .version = {1, 2, 0}, + .version = {1, 3, 0}, .module = THIS_MODULE, .ctr = snapshot_ctr, .dtr = snapshot_dtr, diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 2d1be922aaead6..b71c600128a3ce 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -2507,6 +2507,19 @@ void dm_get(struct mapped_device *md) BUG_ON(test_bit(DMF_FREEING, &md->flags)); } +int dm_hold(struct mapped_device *md) +{ + spin_lock(&_minor_lock); + if (test_bit(DMF_FREEING, &md->flags)) { + spin_unlock(&_minor_lock); + return -EBUSY; + } + dm_get(md); + spin_unlock(&_minor_lock); + return 0; +} +EXPORT_SYMBOL_GPL(dm_hold); + const char *dm_device_name(struct mapped_device *md) { return md->name; diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index ca6d2acc5eb76f..f39722b2836c64 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h @@ -368,6 +368,7 @@ int dm_create(int minor, struct mapped_device **md); */ struct mapped_device *dm_get_md(dev_t dev); void dm_get(struct mapped_device *md); +int dm_hold(struct mapped_device *md); void dm_put(struct mapped_device *md); /* From 2d57a8c4b8ca69d46a7244a9581334542f98bc33 Mon Sep 17 00:00:00 2001 From: "Ivan T. Ivanov" Date: Fri, 6 Mar 2015 17:26:17 +0200 Subject: [PATCH 548/788] spi: qup: Fix cs-num DT property parsing commit 12cb89e37a0c25fae7a0f1d2e4985558db9d0b13 upstream. num-cs is 32 bit property, don't read just upper 16 bits. Fixes: 4a8573abe965 (spi: qup: Remove chip select function) Signed-off-by: Ivan T. Ivanov Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/spi/spi-qup.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c index e7fb5a0d2e8dc3..e27c12a6df9625 100644 --- a/drivers/spi/spi-qup.c +++ b/drivers/spi/spi-qup.c @@ -489,7 +489,7 @@ static int spi_qup_probe(struct platform_device *pdev) struct resource *res; struct device *dev; void __iomem *base; - u32 max_freq, iomode; + u32 max_freq, iomode, num_cs; int ret, irq, size; dev = &pdev->dev; @@ -541,10 +541,11 @@ static int spi_qup_probe(struct platform_device *pdev) } /* use num-cs unless not present or out of range */ - if (of_property_read_u16(dev->of_node, "num-cs", - &master->num_chipselect) || - (master->num_chipselect > SPI_NUM_CHIPSELECTS)) + if (of_property_read_u32(dev->of_node, "num-cs", &num_cs) || + num_cs > SPI_NUM_CHIPSELECTS) master->num_chipselect = SPI_NUM_CHIPSELECTS; + else + master->num_chipselect = num_cs; master->bus_num = pdev->id; master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP; From 68cbdc1ae6dc23f7ee1ffdc59e30a34ccae56997 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 6 Mar 2015 14:42:01 +0200 Subject: [PATCH 549/788] spi: dw-mid: clear BUSY flag fist and test other one commit 854d2f241d71f6ca08ccde30e6c7c2e403363e52 upstream. The logic of DMA completion is broken now since test_and_clear_bit() never returns the other bit is set. It means condition are always false and we have spi_finalize_current_transfer() called per each DMA completion which is wrong. The patch fixes logic by clearing BUSY bit first and then check for the other one. Fixes: 30c8eb52cc4a (spi: dw-mid: split rx and tx callbacks when DMA) Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/spi/spi-dw-mid.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index 22ca08a18b9e73..8076e89f1ea150 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -108,7 +108,8 @@ static void dw_spi_dma_tx_done(void *arg) { struct dw_spi *dws = arg; - if (test_and_clear_bit(TX_BUSY, &dws->dma_chan_busy) & BIT(RX_BUSY)) + clear_bit(TX_BUSY, &dws->dma_chan_busy); + if (test_bit(RX_BUSY, &dws->dma_chan_busy)) return; dw_spi_xfer_done(dws); } @@ -156,7 +157,8 @@ static void dw_spi_dma_rx_done(void *arg) { struct dw_spi *dws = arg; - if (test_and_clear_bit(RX_BUSY, &dws->dma_chan_busy) & BIT(TX_BUSY)) + clear_bit(RX_BUSY, &dws->dma_chan_busy); + if (test_bit(TX_BUSY, &dws->dma_chan_busy)) return; dw_spi_xfer_done(dws); } From 68cbb4c02677613554d9c2267d38cca68bf65cd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 18 Mar 2015 11:27:28 +0100 Subject: [PATCH 550/788] spi: trigger trace event for message-done before mesg->complete MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 391949b6f02121371e3d7d9082c6d17fd9853034 upstream. With spidev the mesg->complete callback points to spidev_complete. Calling this unblocks spidev_sync and so spidev_sync_write finishes. As the struct spi_message just read is a local variable in spidev_sync_write and recording the trace event accesses this message the recording is better done first. The same can happen for spidev_sync_read. This fixes an oops observed on a 3.14-rt system with spidev activity after echo 1 > /sys/kernel/debug/tracing/events/spi/enable . Signed-off-by: Uwe Kleine-König Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/spi/spi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 66a70e9bc7438d..a17f5330333935 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1073,13 +1073,14 @@ void spi_finalize_current_message(struct spi_master *master) "failed to unprepare message: %d\n", ret); } } + + trace_spi_message_done(mesg); + master->cur_msg_prepared = false; mesg->state = NULL; if (mesg->complete) mesg->complete(mesg->context); - - trace_spi_message_done(mesg); } EXPORT_SYMBOL_GPL(spi_finalize_current_message); From 43494e51baa34c20f7d1f7135cc960cacb08c5a4 Mon Sep 17 00:00:00 2001 From: Sergei Antonov Date: Wed, 25 Mar 2015 15:55:34 -0700 Subject: [PATCH 551/788] hfsplus: fix B-tree corruption after insertion at position 0 commit 98cf21c61a7f5419d82f847c4d77bf6e96a76f5f upstream. Fix B-tree corruption when a new record is inserted at position 0 in the node in hfs_brec_insert(). In this case a hfs_brec_update_parent() is called to update the parent index node (if exists) and it is passed hfs_find_data with a search_key containing a newly inserted key instead of the key to be updated. This results in an inconsistent index node. The bug reproduces on my machine after an extents overflow record for the catalog file (CNID=4) is inserted into the extents overflow B-tree. Because of a low (reserved) value of CNID=4, it has to become the first record in the first leaf node. The resulting first leaf node is correct: ---------------------------------------------------- | key0.CNID=4 | key1.CNID=123 | key2.CNID=456, ... | ---------------------------------------------------- But the parent index key0 still contains the previous key CNID=123: ----------------------- | key0.CNID=123 | ... | ----------------------- A change in hfs_brec_insert() makes hfs_brec_update_parent() work correctly by preventing it from getting fd->record=-1 value from __hfs_brec_find(). Along the way, I removed duplicate code with unification of the if condition. The resulting code is equivalent to the original code because node is never 0. Also hfs_brec_update_parent() will now return an error after getting a negative fd->record value. However, the return value of hfs_brec_update_parent() is not checked anywhere in the file and I'm leaving it unchanged by this patch. brec.c lacks error checking after some other calls too, but this issue is of less importance than the one being fixed by this patch. Signed-off-by: Sergei Antonov Cc: Joe Perches Reviewed-by: Vyacheslav Dubeyko Acked-by: Hin-Tak Leung Cc: Anton Altaparmakov Cc: Al Viro Cc: Christoph Hellwig Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/hfsplus/brec.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/fs/hfsplus/brec.c b/fs/hfsplus/brec.c index 6e560d56094b2c..754fdf8c635638 100644 --- a/fs/hfsplus/brec.c +++ b/fs/hfsplus/brec.c @@ -131,13 +131,16 @@ int hfs_brec_insert(struct hfs_find_data *fd, void *entry, int entry_len) hfs_bnode_write(node, entry, data_off + key_len, entry_len); hfs_bnode_dump(node); - if (new_node) { - /* update parent key if we inserted a key - * at the start of the first node - */ - if (!rec && new_node != node) - hfs_brec_update_parent(fd); + /* + * update parent key if we inserted a key + * at the start of the node and it is not the new node + */ + if (!rec && new_node != node) { + hfs_bnode_read_key(node, fd->search_key, data_off + size); + hfs_brec_update_parent(fd); + } + if (new_node) { hfs_bnode_put(fd->bnode); if (!new_node->parent) { hfs_btree_inc_height(tree); @@ -168,9 +171,6 @@ int hfs_brec_insert(struct hfs_find_data *fd, void *entry, int entry_len) goto again; } - if (!rec) - hfs_brec_update_parent(fd); - return 0; } @@ -370,6 +370,8 @@ static int hfs_brec_update_parent(struct hfs_find_data *fd) if (IS_ERR(parent)) return PTR_ERR(parent); __hfs_brec_find(parent, fd, hfs_find_rec_by_key); + if (fd->record < 0) + return -ENOENT; hfs_bnode_dump(parent); rec = fd->record; From 88519acc5f9c96fe6661157ddfa275e743aca4c1 Mon Sep 17 00:00:00 2001 From: Mahesh Salgaonkar Date: Tue, 17 Mar 2015 16:14:41 +0530 Subject: [PATCH 552/788] powerpc/book3s: Fix the MCE code to use CONFIG_KVM_BOOK3S_64_HANDLER commit 44d5f6f5901e996744858c175baee320ccf1eda3 upstream. commit id 2ba9f0d has changed CONFIG_KVM_BOOK3S_64_HV to tristate to allow HV/PR bits to be built as modules. But the MCE code still depends on CONFIG_KVM_BOOK3S_64_HV which is wrong. When user selects CONFIG_KVM_BOOK3S_64_HV=m to build HV/PR bits as a separate module the relevant MCE code gets excluded. This patch fixes the MCE code to use CONFIG_KVM_BOOK3S_64_HANDLER. This makes sure that the relevant MCE code is included when HV/PR bits are built as a separate modules. Fixes: 2ba9f0d88750 ("kvm: powerpc: book3s: Support building HV and PR KVM as module") Signed-off-by: Mahesh Salgaonkar Acked-by: Paul Mackerras Signed-off-by: Michael Ellerman Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/kernel/exceptions-64s.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index c2df8150bd7a04..9519e6bdc6d75c 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -1408,7 +1408,7 @@ machine_check_handle_early: bne 9f /* continue in V mode if we are. */ 5: -#ifdef CONFIG_KVM_BOOK3S_64_HV +#ifdef CONFIG_KVM_BOOK3S_64_HANDLER /* * We are coming from kernel context. Check if we are coming from * guest. if yes, then we can continue. We will fall through From 4b38884078bea8fe24486845caa3883fa4fa5460 Mon Sep 17 00:00:00 2001 From: Keerthy Date: Tue, 17 Mar 2015 15:56:04 +0530 Subject: [PATCH 553/788] regulator: palmas: Correct TPS659038 register definition for REGEN2 commit e03826d5045e81a66a4fad7be9a8ecdaeb7911cf upstream. The register offset for REGEN2_CTRL in different for TPS659038 chip as when compared with other Palmas family PMICs. In the case of TPS659038 the wrong offset pointed to PLLEN_CTRL and was causing a hang. Correcting the same. Signed-off-by: Keerthy Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/regulator/palmas-regulator.c | 4 ++++ include/linux/mfd/palmas.h | 3 +++ 2 files changed, 7 insertions(+) diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c index 9205f433573cc1..18198316b6cf15 100644 --- a/drivers/regulator/palmas-regulator.c +++ b/drivers/regulator/palmas-regulator.c @@ -1572,6 +1572,10 @@ static int palmas_regulators_probe(struct platform_device *pdev) if (!pmic) return -ENOMEM; + if (of_device_is_compatible(node, "ti,tps659038-pmic")) + palmas_generic_regs_info[PALMAS_REG_REGEN2].ctrl_addr = + TPS659038_REGEN2_CTRL; + pmic->dev = &pdev->dev; pmic->palmas = palmas; palmas->pmic = pmic; diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h index fb0390a1a498f2..ee7b1ce7a6f8f4 100644 --- a/include/linux/mfd/palmas.h +++ b/include/linux/mfd/palmas.h @@ -2999,6 +2999,9 @@ enum usb_irq_events { #define PALMAS_GPADC_TRIM15 0x0E #define PALMAS_GPADC_TRIM16 0x0F +/* TPS659038 regen2_ctrl offset iss different from palmas */ +#define TPS659038_REGEN2_CTRL 0x12 + /* TPS65917 Interrupt registers */ /* Registers for function INTERRUPT */ From 7521625c56d08fd27c20cf88423b41067bb5a03c Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Mon, 23 Mar 2015 15:06:50 +0000 Subject: [PATCH 554/788] arm64: Use the reserved TTBR0 if context switching to the init_mm commit e53f21bce4d35a93b23d8fa1a840860f6c74f59e upstream. The idle_task_exit() function may call switch_mm() with next == &init_mm. On arm64, init_mm.pgd cannot be used for user mappings, so this patch simply sets the reserved TTBR0. Reported-by: Jon Medhurst (Tixy) Tested-by: Jon Medhurst (Tixy) Signed-off-by: Catalin Marinas Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/mmu_context.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h index a9eee33dfa62dc..101a42bde728a8 100644 --- a/arch/arm64/include/asm/mmu_context.h +++ b/arch/arm64/include/asm/mmu_context.h @@ -151,6 +151,15 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next, { unsigned int cpu = smp_processor_id(); + /* + * init_mm.pgd does not contain any user mappings and it is always + * active for kernel addresses in TTBR1. Just set the reserved TTBR0. + */ + if (next == &init_mm) { + cpu_set_reserved_ttbr0(); + return; + } + if (!cpumask_test_and_set_cpu(cpu, mm_cpumask(next)) || prev != next) check_and_switch_context(next, tsk); } From f179d6a3e20afc890989c96df3d873daee0fed09 Mon Sep 17 00:00:00 2001 From: Steve Capper Date: Sun, 22 Mar 2015 14:51:51 +0000 Subject: [PATCH 555/788] arm64: percpu: Make this_cpu accessors pre-empt safe commit f3eab7184ddcd4867cf42e3274ba24a66e1e093d upstream. this_cpu operations were implemented for arm64 in: 5284e1b arm64: xchg: Implement cmpxchg_double f97fc81 arm64: percpu: Implement this_cpu operations Unfortunately, it is possible for pre-emption to take place between address generation and data access. This can lead to cases where data is being manipulated by this_cpu for a different CPU than it was called on. Which effectively breaks the spec. This patch disables pre-emption for the this_cpu operations guaranteeing that address generation and data manipulation take place without a pre-emption in-between. Fixes: 5284e1b4bc8a ("arm64: xchg: Implement cmpxchg_double") Fixes: f97fc810798c ("arm64: percpu: Implement this_cpu operations") Reported-by: Mark Rutland Acked-by: Will Deacon Signed-off-by: Steve Capper [catalin.marinas@arm.com: remove space after type cast] Signed-off-by: Catalin Marinas Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/cmpxchg.h | 32 +++++++++++++++++------ arch/arm64/include/asm/percpu.h | 44 ++++++++++++++++++++++++-------- 2 files changed, 57 insertions(+), 19 deletions(-) diff --git a/arch/arm64/include/asm/cmpxchg.h b/arch/arm64/include/asm/cmpxchg.h index cb9593079f2976..d8c25b7b18fbf4 100644 --- a/arch/arm64/include/asm/cmpxchg.h +++ b/arch/arm64/include/asm/cmpxchg.h @@ -246,14 +246,30 @@ static inline unsigned long __cmpxchg_mb(volatile void *ptr, unsigned long old, __ret; \ }) -#define this_cpu_cmpxchg_1(ptr, o, n) cmpxchg_local(raw_cpu_ptr(&(ptr)), o, n) -#define this_cpu_cmpxchg_2(ptr, o, n) cmpxchg_local(raw_cpu_ptr(&(ptr)), o, n) -#define this_cpu_cmpxchg_4(ptr, o, n) cmpxchg_local(raw_cpu_ptr(&(ptr)), o, n) -#define this_cpu_cmpxchg_8(ptr, o, n) cmpxchg_local(raw_cpu_ptr(&(ptr)), o, n) - -#define this_cpu_cmpxchg_double_8(ptr1, ptr2, o1, o2, n1, n2) \ - cmpxchg_double_local(raw_cpu_ptr(&(ptr1)), raw_cpu_ptr(&(ptr2)), \ - o1, o2, n1, n2) +#define _protect_cmpxchg_local(pcp, o, n) \ +({ \ + typeof(*raw_cpu_ptr(&(pcp))) __ret; \ + preempt_disable(); \ + __ret = cmpxchg_local(raw_cpu_ptr(&(pcp)), o, n); \ + preempt_enable(); \ + __ret; \ +}) + +#define this_cpu_cmpxchg_1(ptr, o, n) _protect_cmpxchg_local(ptr, o, n) +#define this_cpu_cmpxchg_2(ptr, o, n) _protect_cmpxchg_local(ptr, o, n) +#define this_cpu_cmpxchg_4(ptr, o, n) _protect_cmpxchg_local(ptr, o, n) +#define this_cpu_cmpxchg_8(ptr, o, n) _protect_cmpxchg_local(ptr, o, n) + +#define this_cpu_cmpxchg_double_8(ptr1, ptr2, o1, o2, n1, n2) \ +({ \ + int __ret; \ + preempt_disable(); \ + __ret = cmpxchg_double_local( raw_cpu_ptr(&(ptr1)), \ + raw_cpu_ptr(&(ptr2)), \ + o1, o2, n1, n2); \ + preempt_enable(); \ + __ret; \ +}) #define cmpxchg64(ptr,o,n) cmpxchg((ptr),(o),(n)) #define cmpxchg64_local(ptr,o,n) cmpxchg_local((ptr),(o),(n)) diff --git a/arch/arm64/include/asm/percpu.h b/arch/arm64/include/asm/percpu.h index 09da25bc596fd0..4fde8c1df97ffb 100644 --- a/arch/arm64/include/asm/percpu.h +++ b/arch/arm64/include/asm/percpu.h @@ -204,25 +204,47 @@ static inline unsigned long __percpu_xchg(void *ptr, unsigned long val, return ret; } +#define _percpu_read(pcp) \ +({ \ + typeof(pcp) __retval; \ + preempt_disable(); \ + __retval = (typeof(pcp))__percpu_read(raw_cpu_ptr(&(pcp)), \ + sizeof(pcp)); \ + preempt_enable(); \ + __retval; \ +}) + +#define _percpu_write(pcp, val) \ +do { \ + preempt_disable(); \ + __percpu_write(raw_cpu_ptr(&(pcp)), (unsigned long)(val), \ + sizeof(pcp)); \ + preempt_enable(); \ +} while(0) \ + +#define _pcp_protect(operation, pcp, val) \ +({ \ + typeof(pcp) __retval; \ + preempt_disable(); \ + __retval = (typeof(pcp))operation(raw_cpu_ptr(&(pcp)), \ + (val), sizeof(pcp)); \ + preempt_enable(); \ + __retval; \ +}) + #define _percpu_add(pcp, val) \ - __percpu_add(raw_cpu_ptr(&(pcp)), val, sizeof(pcp)) + _pcp_protect(__percpu_add, pcp, val) -#define _percpu_add_return(pcp, val) (typeof(pcp)) (_percpu_add(pcp, val)) +#define _percpu_add_return(pcp, val) _percpu_add(pcp, val) #define _percpu_and(pcp, val) \ - __percpu_and(raw_cpu_ptr(&(pcp)), val, sizeof(pcp)) + _pcp_protect(__percpu_and, pcp, val) #define _percpu_or(pcp, val) \ - __percpu_or(raw_cpu_ptr(&(pcp)), val, sizeof(pcp)) - -#define _percpu_read(pcp) (typeof(pcp)) \ - (__percpu_read(raw_cpu_ptr(&(pcp)), sizeof(pcp))) - -#define _percpu_write(pcp, val) \ - __percpu_write(raw_cpu_ptr(&(pcp)), (unsigned long)(val), sizeof(pcp)) + _pcp_protect(__percpu_or, pcp, val) #define _percpu_xchg(pcp, val) (typeof(pcp)) \ - (__percpu_xchg(raw_cpu_ptr(&(pcp)), (unsigned long)(val), sizeof(pcp))) + _pcp_protect(__percpu_xchg, pcp, (unsigned long)(val)) #define this_cpu_add_1(pcp, val) _percpu_add(pcp, val) #define this_cpu_add_2(pcp, val) _percpu_add(pcp, val) From 78db0115d87fc97513a84fb739ee74d1af34aef2 Mon Sep 17 00:00:00 2001 From: Tyrel Datwyler Date: Wed, 4 Mar 2015 11:59:33 -0800 Subject: [PATCH 556/788] powerpc/pseries: Little endian fixes for post mobility device tree update commit f6ff04149637723261aa4738958b0098b929ee9e upstream. We currently use the device tree update code in the kernel after resuming from a suspend operation to re-sync the kernels view of the device tree with that of the hypervisor. The code as it stands is not endian safe as it relies on parsing buffers returned by RTAS calls that thusly contains data in big endian format. This patch annotates variables and structure members with __be types as well as performing necessary byte swaps to cpu endian for data that needs to be parsed. Signed-off-by: Tyrel Datwyler Cc: Nathan Fontenot Cc: Cyril Bur Signed-off-by: Michael Ellerman Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/platforms/pseries/mobility.c | 44 ++++++++++++----------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/arch/powerpc/platforms/pseries/mobility.c b/arch/powerpc/platforms/pseries/mobility.c index e7cb6d4a871ae7..f8c9ff7886e1ce 100644 --- a/arch/powerpc/platforms/pseries/mobility.c +++ b/arch/powerpc/platforms/pseries/mobility.c @@ -25,10 +25,10 @@ static struct kobject *mobility_kobj; struct update_props_workarea { - u32 phandle; - u32 state; - u64 reserved; - u32 nprops; + __be32 phandle; + __be32 state; + __be64 reserved; + __be32 nprops; } __packed; #define NODE_ACTION_MASK 0xff000000 @@ -54,11 +54,11 @@ static int mobility_rtas_call(int token, char *buf, s32 scope) return rc; } -static int delete_dt_node(u32 phandle) +static int delete_dt_node(__be32 phandle) { struct device_node *dn; - dn = of_find_node_by_phandle(phandle); + dn = of_find_node_by_phandle(be32_to_cpu(phandle)); if (!dn) return -ENOENT; @@ -127,7 +127,7 @@ static int update_dt_property(struct device_node *dn, struct property **prop, return 0; } -static int update_dt_node(u32 phandle, s32 scope) +static int update_dt_node(__be32 phandle, s32 scope) { struct update_props_workarea *upwa; struct device_node *dn; @@ -136,6 +136,7 @@ static int update_dt_node(u32 phandle, s32 scope) char *prop_data; char *rtas_buf; int update_properties_token; + u32 nprops; u32 vd; update_properties_token = rtas_token("ibm,update-properties"); @@ -146,7 +147,7 @@ static int update_dt_node(u32 phandle, s32 scope) if (!rtas_buf) return -ENOMEM; - dn = of_find_node_by_phandle(phandle); + dn = of_find_node_by_phandle(be32_to_cpu(phandle)); if (!dn) { kfree(rtas_buf); return -ENOENT; @@ -162,6 +163,7 @@ static int update_dt_node(u32 phandle, s32 scope) break; prop_data = rtas_buf + sizeof(*upwa); + nprops = be32_to_cpu(upwa->nprops); /* On the first call to ibm,update-properties for a node the * the first property value descriptor contains an empty @@ -170,17 +172,17 @@ static int update_dt_node(u32 phandle, s32 scope) */ if (*prop_data == 0) { prop_data++; - vd = *(u32 *)prop_data; + vd = be32_to_cpu(*(__be32 *)prop_data); prop_data += vd + sizeof(vd); - upwa->nprops--; + nprops--; } - for (i = 0; i < upwa->nprops; i++) { + for (i = 0; i < nprops; i++) { char *prop_name; prop_name = prop_data; prop_data += strlen(prop_name) + 1; - vd = *(u32 *)prop_data; + vd = be32_to_cpu(*(__be32 *)prop_data); prop_data += sizeof(vd); switch (vd) { @@ -212,13 +214,13 @@ static int update_dt_node(u32 phandle, s32 scope) return 0; } -static int add_dt_node(u32 parent_phandle, u32 drc_index) +static int add_dt_node(__be32 parent_phandle, __be32 drc_index) { struct device_node *dn; struct device_node *parent_dn; int rc; - parent_dn = of_find_node_by_phandle(parent_phandle); + parent_dn = of_find_node_by_phandle(be32_to_cpu(parent_phandle)); if (!parent_dn) return -ENOENT; @@ -237,7 +239,7 @@ static int add_dt_node(u32 parent_phandle, u32 drc_index) int pseries_devicetree_update(s32 scope) { char *rtas_buf; - u32 *data; + __be32 *data; int update_nodes_token; int rc; @@ -254,17 +256,17 @@ int pseries_devicetree_update(s32 scope) if (rc && rc != 1) break; - data = (u32 *)rtas_buf + 4; - while (*data & NODE_ACTION_MASK) { + data = (__be32 *)rtas_buf + 4; + while (be32_to_cpu(*data) & NODE_ACTION_MASK) { int i; - u32 action = *data & NODE_ACTION_MASK; - int node_count = *data & NODE_COUNT_MASK; + u32 action = be32_to_cpu(*data) & NODE_ACTION_MASK; + u32 node_count = be32_to_cpu(*data) & NODE_COUNT_MASK; data++; for (i = 0; i < node_count; i++) { - u32 phandle = *data++; - u32 drc_index; + __be32 phandle = *data++; + __be32 drc_index; switch (action) { case DELETE_DT_NODE: From db5bb38e9e628c79bba709e0de6dea1863597caf Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Wed, 17 Dec 2014 19:06:31 -0600 Subject: [PATCH 557/788] powerpc/mpc85xx: Add ranges to etsec2 nodes commit bb344ca5b90df62b1a3b7a35c6a9d00b306a170d upstream. Commit 746c9e9f92dd "of/base: Fix PowerPC address parsing hack" limited the applicability of the workaround whereby a missing ranges is treated as an empty ranges. This workaround was hiding a bug in the etsec2 device tree nodes, which have children with reg, but did not have ranges. Signed-off-by: Scott Wood Reported-by: Alexander Graf Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/boot/dts/fsl/pq3-etsec2-0.dtsi | 1 + arch/powerpc/boot/dts/fsl/pq3-etsec2-1.dtsi | 1 + arch/powerpc/boot/dts/fsl/pq3-etsec2-2.dtsi | 1 + 3 files changed, 3 insertions(+) diff --git a/arch/powerpc/boot/dts/fsl/pq3-etsec2-0.dtsi b/arch/powerpc/boot/dts/fsl/pq3-etsec2-0.dtsi index 1382fec9e8c5f8..7fcb1ac0f2325f 100644 --- a/arch/powerpc/boot/dts/fsl/pq3-etsec2-0.dtsi +++ b/arch/powerpc/boot/dts/fsl/pq3-etsec2-0.dtsi @@ -50,6 +50,7 @@ ethernet@b0000 { fsl,num_tx_queues = <0x8>; fsl,magic-packet; local-mac-address = [ 00 00 00 00 00 00 ]; + ranges; queue-group@b0000 { #address-cells = <1>; diff --git a/arch/powerpc/boot/dts/fsl/pq3-etsec2-1.dtsi b/arch/powerpc/boot/dts/fsl/pq3-etsec2-1.dtsi index 221cd2ea5b3124..9f25427c152785 100644 --- a/arch/powerpc/boot/dts/fsl/pq3-etsec2-1.dtsi +++ b/arch/powerpc/boot/dts/fsl/pq3-etsec2-1.dtsi @@ -50,6 +50,7 @@ ethernet@b1000 { fsl,num_tx_queues = <0x8>; fsl,magic-packet; local-mac-address = [ 00 00 00 00 00 00 ]; + ranges; queue-group@b1000 { #address-cells = <1>; diff --git a/arch/powerpc/boot/dts/fsl/pq3-etsec2-2.dtsi b/arch/powerpc/boot/dts/fsl/pq3-etsec2-2.dtsi index 61456c317609ce..cd7c318ab131af 100644 --- a/arch/powerpc/boot/dts/fsl/pq3-etsec2-2.dtsi +++ b/arch/powerpc/boot/dts/fsl/pq3-etsec2-2.dtsi @@ -49,6 +49,7 @@ ethernet@b2000 { fsl,num_tx_queues = <0x8>; fsl,magic-packet; local-mac-address = [ 00 00 00 00 00 00 ]; + ranges; queue-group@b2000 { #address-cells = <1>; From e8f86a9ba285bbf50aac67e03726e33bbf97f685 Mon Sep 17 00:00:00 2001 From: Markos Chandras Date: Thu, 19 Mar 2015 10:28:14 +0000 Subject: [PATCH 558/788] net: ethernet: pcnet32: Setup the SRAM and NOUFLO on Am79C97{3, 5} commit 87f966d97b89774162df04d2106c6350c8fe4cb3 upstream. On a MIPS Malta board, tons of fifo underflow errors have been observed when using u-boot as bootloader instead of YAMON. The reason for that is that YAMON used to set the pcnet device to SRAM mode but u-boot does not. As a result, the default Tx threshold (64 bytes) is now too small to keep the fifo relatively used and it can result to Tx fifo underflow errors. As a result of which, it's best to setup the SRAM on supported controllers so we can always use the NOUFLO bit. Cc: Cc: Cc: Don Fry Signed-off-by: Markos Chandras Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/amd/pcnet32.c | 31 ++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/amd/pcnet32.c b/drivers/net/ethernet/amd/pcnet32.c index e2e3aaf501a20f..30f088be6a1ac7 100644 --- a/drivers/net/ethernet/amd/pcnet32.c +++ b/drivers/net/ethernet/amd/pcnet32.c @@ -1543,7 +1543,7 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev) { struct pcnet32_private *lp; int i, media; - int fdx, mii, fset, dxsuflo; + int fdx, mii, fset, dxsuflo, sram; int chip_version; char *chipname; struct net_device *dev; @@ -1580,7 +1580,7 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev) } /* initialize variables */ - fdx = mii = fset = dxsuflo = 0; + fdx = mii = fset = dxsuflo = sram = 0; chip_version = (chip_version >> 12) & 0xffff; switch (chip_version) { @@ -1613,6 +1613,7 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev) chipname = "PCnet/FAST III 79C973"; /* PCI */ fdx = 1; mii = 1; + sram = 1; break; case 0x2626: chipname = "PCnet/Home 79C978"; /* PCI */ @@ -1636,6 +1637,7 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev) chipname = "PCnet/FAST III 79C975"; /* PCI */ fdx = 1; mii = 1; + sram = 1; break; case 0x2628: chipname = "PCnet/PRO 79C976"; @@ -1664,6 +1666,31 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev) dxsuflo = 1; } + /* + * The Am79C973/Am79C975 controllers come with 12K of SRAM + * which we can use for the Tx/Rx buffers but most importantly, + * the use of SRAM allow us to use the BCR18:NOUFLO bit to avoid + * Tx fifo underflows. + */ + if (sram) { + /* + * The SRAM is being configured in two steps. First we + * set the SRAM size in the BCR25:SRAM_SIZE bits. According + * to the datasheet, each bit corresponds to a 512-byte + * page so we can have at most 24 pages. The SRAM_SIZE + * holds the value of the upper 8 bits of the 16-bit SRAM size. + * The low 8-bits start at 0x00 and end at 0xff. So the + * address range is from 0x0000 up to 0x17ff. Therefore, + * the SRAM_SIZE is set to 0x17. The next step is to set + * the BCR26:SRAM_BND midway through so the Tx and Rx + * buffers can share the SRAM equally. + */ + a->write_bcr(ioaddr, 25, 0x17); + a->write_bcr(ioaddr, 26, 0xc); + /* And finally enable the NOUFLO bit */ + a->write_bcr(ioaddr, 18, a->read_bcr(ioaddr, 18) | (1 << 11)); + } + dev = alloc_etherdev(sizeof(*lp)); if (!dev) { ret = -ENOMEM; From 50bc7bc7287391f6ab420ae86814751d20e945a9 Mon Sep 17 00:00:00 2001 From: Ameya Palande <2ameya@gmail.com> Date: Thu, 26 Feb 2015 12:05:51 -0800 Subject: [PATCH 559/788] mfd: kempld-core: Fix callback return value check commit c8648508ebfc597058d2cd00b6c539110264a167 upstream. On success, callback function returns 0. So invert the if condition check so that we can break out of loop. Signed-off-by: Ameya Palande <2ameya@gmail.com> Signed-off-by: Lee Jones Signed-off-by: Greg Kroah-Hartman --- drivers/mfd/kempld-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/kempld-core.c b/drivers/mfd/kempld-core.c index f38ec424872e36..5615522f8d628b 100644 --- a/drivers/mfd/kempld-core.c +++ b/drivers/mfd/kempld-core.c @@ -739,7 +739,7 @@ static int __init kempld_init(void) for (id = kempld_dmi_table; id->matches[0].slot != DMI_NONE; id++) if (strstr(id->ident, force_device_id)) - if (id->callback && id->callback(id)) + if (id->callback && !id->callback(id)) break; if (id->matches[0].slot == DMI_NONE) return -ENODEV; From 31ad7cdd37a0a9eb80318f8aa567064f3e7b5b0b Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 13 Apr 2015 14:04:12 +0200 Subject: [PATCH 560/788] Linux 3.19.4 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 713bf263952fe9..2ef20781ad25b3 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 19 -SUBLEVEL = 3 +SUBLEVEL = 4 EXTRAVERSION = NAME = Diseased Newt From ac01314e433dda3b127ecf90e9d379c423807da5 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Sun, 14 Sep 2014 20:29:41 +0100 Subject: [PATCH 561/788] ARM: dts: exynos: Only build DTS files for subarches which are enabled Currently we build all Exynos dtb files if CONFIG_ARCH_EXYNOS is enabled, even if the corresponding CONFIG_ARCH_EXYNOS[345] subarch is not enabled, meaning we may build dtbs for things which can't actually run in a particular configuration. Switch instead to using the subarch option, based purely on the filename which I assume matches. Note that there doesn't apear to be any Exynos3 board files right now (or I have somehow missed them). Signed-off-by: Ian Campbell Cc: Russell King Cc: Kukjin Kim Cc: linux-arm-kernel@lists.infradead.org Cc: linux-samsung-soc@vger.kernel.org --- arch/arm/boot/dts/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index 91bd5bd628576d..7601a40101c337 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -76,7 +76,7 @@ dtb-$(CONFIG_ARCH_BRCMSTB) += \ dtb-$(CONFIG_ARCH_DAVINCI) += da850-enbw-cmc.dtb \ da850-evm.dtb dtb-$(CONFIG_ARCH_EFM32) += efm32gg-dk3750.dtb -dtb-$(CONFIG_ARCH_EXYNOS) += exynos3250-monk.dtb \ +dtb-$(CONFIG_ARCH_EXYNOS4) += exynos3250-monk.dtb \ exynos3250-rinato.dtb \ exynos4210-origen.dtb \ exynos4210-smdkv310.dtb \ @@ -88,8 +88,8 @@ dtb-$(CONFIG_ARCH_EXYNOS) += exynos3250-monk.dtb \ exynos4412-origen.dtb \ exynos4412-smdk4412.dtb \ exynos4412-tiny4412.dtb \ - exynos4412-trats2.dtb \ - exynos5250-arndale.dtb \ + exynos4412-trats2.dtb +dtb-$(CONFIG_ARCH_EXYNOS5) += exynos5250-arndale.dtb \ exynos5250-smdk5250.dtb \ exynos5250-snow.dtb \ exynos5250-spring.dtb \ From 96a84bbb49af6f61977914ab834bc2eab9c6b6f3 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Tue, 11 Feb 2014 15:42:07 +0100 Subject: [PATCH 562/788] usb3503: Add 'usb3503-gpio-waittime' property to device tree --- .../devicetree/bindings/usb/usb3503.txt | 2 ++ drivers/usb/misc/usb3503.c | 17 +++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/usb3503.txt b/Documentation/devicetree/bindings/usb/usb3503.txt index 52493b1480e275..b67a7536f0efe4 100644 --- a/Documentation/devicetree/bindings/usb/usb3503.txt +++ b/Documentation/devicetree/bindings/usb/usb3503.txt @@ -23,6 +23,8 @@ Optional properties: pins (optional, if not provided, driver will not set rate of the REFCLK signal and assume that a value from the primary reference clock frequencies table is used) +- usb3503-gpio-waittime: Some boards need a longer delay on 'reset' and 'connect' GPIO. + Use this to set extra wait time (in milliseconds). Examples: usb3503@08 { diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index 258d2f546e430e..6c6cf540a3c297 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -59,10 +59,11 @@ struct usb3503 { struct regmap *regmap; struct device *dev; struct clk *clk; - u8 port_off_mask; int gpio_intn; int gpio_reset; int gpio_connect; + unsigned gpio_waittime; + u8 port_off_mask; bool secondary_ref_clk; }; @@ -75,9 +76,13 @@ static int usb3503_reset(struct usb3503 *hub, int state) gpio_set_value_cansleep(hub->gpio_reset, state); /* Wait T_HUBINIT == 4ms for hub logic to stabilize */ - if (state) + if (state) { usleep_range(4000, 10000); + if (hub->gpio_waittime) + msleep(hub->gpio_waittime); + } + return 0; } @@ -131,6 +136,9 @@ static int usb3503_connect(struct usb3503 *hub) if (gpio_is_valid(hub->gpio_connect)) gpio_set_value_cansleep(hub->gpio_connect, 1); + if (hub->gpio_waittime) + msleep(hub->gpio_waittime); + hub->mode = USB3503_MODE_HUB; dev_info(dev, "switched to HUB mode\n"); @@ -175,6 +183,7 @@ static int usb3503_probe(struct usb3503 *hub) struct device_node *np = dev->of_node; int err; u32 mode = USB3503_MODE_HUB; + u32 waittime = 0; const u32 *property; int len; @@ -257,8 +266,12 @@ static int usb3503_probe(struct usb3503 *hub) hub->gpio_reset = of_get_named_gpio(np, "reset-gpios", 0); if (hub->gpio_reset == -EPROBE_DEFER) return -EPROBE_DEFER; + of_property_read_u32(np, "initial-mode", &mode); hub->mode = mode; + + of_property_read_u32(np, "usb3503-gpio-waittime", &waittime); + hub->gpio_waittime = waittime; } if (hub->port_off_mask && !hub->regmap) From 995c90eaa2365342a500f156b7b3b90ad8d65345 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Tue, 11 Feb 2014 16:28:18 +0100 Subject: [PATCH 563/788] usb3503: add more register defines --- drivers/usb/misc/usb3503.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index 6c6cf540a3c297..1257ae371e65ba 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -38,17 +38,38 @@ #define USB3503_CFG1 0x06 #define USB3503_SELF_BUS_PWR (1 << 7) +#define USB3503_MTT_ENABLE (1 << 4) +#define USB3503_OCS_INDIVIDUAL (1 << 1) +#define USB3503_OCS_DISABLE (3 << 1) +#define USB3503_PPRTPWR_INDIVIDUAL (1 << 0) #define USB3503_CFG2 0x07 #define USB3503_CFG3 0x08 #define USB3503_NRD 0x09 #define USB3503_PDS 0x0a +#define USB3503_OCS 0xe6 #define USB3503_SP_ILOCK 0xe7 +#define USB3503_OCS_PINSEL (1 << 5) +#define USB3503_PRTPWR_PINSEL (1 << 4) #define USB3503_SPILOCK_CONNECT (1 << 1) #define USB3503_SPILOCK_CONFIG (1 << 0) +/* Varisense/squelch setting for downstream ports: */ +#define USB3503_VSNSUP3 0xf4 +#define DN3_SQUELCH_120 (6 << 0) +#define USB3503_VSNSUP21 0xf5 +#define DN2_SQUELCH_120 (6 << 4) +#define DN1_SQUELCH_120 (6 << 0) + +/* Signaling drive strength boost (downstream ports): */ +#define USB3503_BSTUP3 0xf6 +#define BOOST_IOOUT3_30 (6 << 0) +#define USB3503_BSTUP21 0xf8 +#define BOOST_IOOUT2_30 (6 << 4) +#define BOOST_IOOUT1_30 (6 << 0) + #define USB3503_CFGP 0xee #define USB3503_CLKSUSP (1 << 7) From 867d49e286e632491b3d29b0a4ef0e9441c4fc88 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Tue, 11 Feb 2014 16:57:10 +0100 Subject: [PATCH 564/788] usb3503: write to PDS and CFG1 register instead of updating --- drivers/usb/misc/usb3503.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index 1257ae371e65ba..9d926a98e0a60c 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -126,9 +126,7 @@ static int usb3503_connect(struct usb3503 *hub) /* PDS : Set the ports which are disabled in self-powered mode. */ if (hub->port_off_mask) { - err = regmap_update_bits(hub->regmap, USB3503_PDS, - hub->port_off_mask, - hub->port_off_mask); + err = regmap_write(hub->regmap, USB3503_PDS, hub->port_off_mask); if (err < 0) { dev_err(dev, "PDS failed (%d)\n", err); return err; @@ -136,9 +134,7 @@ static int usb3503_connect(struct usb3503 *hub) } /* CFG1 : Set SELF_BUS_PWR, this enables self-powered operation. */ - err = regmap_update_bits(hub->regmap, USB3503_CFG1, - USB3503_SELF_BUS_PWR, - USB3503_SELF_BUS_PWR); + err = regmap_write(hub->regmap, USB3503_CFG1, USB3503_SELF_BUS_PWR); if (err < 0) { dev_err(dev, "CFG1 failed (%d)\n", err); return err; From f66676e683ae8c8173b8387b5047902feabc5a5d Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sat, 15 Feb 2014 02:18:32 +0100 Subject: [PATCH 565/788] usb3503: set some more bits in the CFG1 register --- drivers/usb/misc/usb3503.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index 9d926a98e0a60c..78256518d5022b 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -39,6 +39,7 @@ #define USB3503_CFG1 0x06 #define USB3503_SELF_BUS_PWR (1 << 7) #define USB3503_MTT_ENABLE (1 << 4) +#define USB3503_CFG1_RESDEF (1 << 3) #define USB3503_OCS_INDIVIDUAL (1 << 1) #define USB3503_OCS_DISABLE (3 << 1) #define USB3503_PPRTPWR_INDIVIDUAL (1 << 0) @@ -133,8 +134,12 @@ static int usb3503_connect(struct usb3503 *hub) } } - /* CFG1 : Set SELF_BUS_PWR, this enables self-powered operation. */ - err = regmap_write(hub->regmap, USB3503_CFG1, USB3503_SELF_BUS_PWR); + /* CFG1 : Set SELF_BUS_PWR, this enables self-powered operation. + * Set MTT_ENABLE, this uses a dedicated TT for each port. + * Set CFG1_RESDEF, this is reserved according to the datasheet, but + * it is set by default, so we set it here as well. */ + err = regmap_write(hub->regmap, USB3503_CFG1, + USB3503_SELF_BUS_PWR | USB3503_MTT_ENABLE | USB3503_CFG1_RESDEF); if (err < 0) { dev_err(dev, "CFG1 failed (%d)\n", err); return err; From 1a55664707446f5f24e34af72a8ddb3c6caa9af9 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sun, 21 Sep 2014 01:57:54 +0200 Subject: [PATCH 566/788] usb3503: add optional ext-regulator property On some developer boards where the USB3503 is used, additional blocks are connected to it. E.g. on the Hardkernel ODROID-U2 a LAN97xx (ethernet with USB hub combo) is connected to it. The LAN97xx needs its regulator enabled, but in particular this regulator also needs to be switched off again on shutdown, otherwise it the LAN97xx is no longer detected after a reboot. This patch adds a new DT property 'ext-supply' so that one can specify which regulator has to be enabled on probe / disabled on shutdown. --- .../devicetree/bindings/usb/usb3503.txt | 2 + drivers/usb/misc/usb3503.c | 57 +++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/Documentation/devicetree/bindings/usb/usb3503.txt b/Documentation/devicetree/bindings/usb/usb3503.txt index b67a7536f0efe4..a1da2b84fdea7f 100644 --- a/Documentation/devicetree/bindings/usb/usb3503.txt +++ b/Documentation/devicetree/bindings/usb/usb3503.txt @@ -25,6 +25,8 @@ Optional properties: clock frequencies table is used) - usb3503-gpio-waittime: Some boards need a longer delay on 'reset' and 'connect' GPIO. Use this to set extra wait time (in milliseconds). +- ext-supply: Optional property to specify a regulator that is switched + on during probe and off when the device is shutdown. Examples: usb3503@08 { diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index 78256518d5022b..f5e6534c2c8133 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -28,6 +28,7 @@ #include #include #include +#include #define USB3503_VIDL 0x00 #define USB3503_VIDM 0x01 @@ -81,6 +82,7 @@ struct usb3503 { struct regmap *regmap; struct device *dev; struct clk *clk; + struct regulator *reg_ext; int gpio_intn; int gpio_reset; int gpio_connect; @@ -89,6 +91,23 @@ struct usb3503 { bool secondary_ref_clk; }; +static int usb3503_ext_regulator(struct usb3503 *hub, int state) +{ + int ret; + + if (!hub->reg_ext) + return 0; + + if (state) { + ret = regulator_enable(hub->reg_ext); + } else { + regulator_disable(hub->reg_ext); + ret = 0; + } + + return ret; +} + static int usb3503_reset(struct usb3503 *hub, int state) { if (!state && gpio_is_valid(hub->gpio_connect)) @@ -217,6 +236,7 @@ static int usb3503_probe(struct usb3503 *hub) hub->mode = pdata->initial_mode; } else if (np) { struct clk *clk; + struct regulator *reg; hub->port_off_mask = 0; clk = devm_clk_get(dev, "refclk"); @@ -294,6 +314,11 @@ static int usb3503_probe(struct usb3503 *hub) of_property_read_u32(np, "usb3503-gpio-waittime", &waittime); hub->gpio_waittime = waittime; + + reg = devm_regulator_get_optional(dev, "ext"); + if (!IS_ERR_OR_NULL(reg)) { + hub->reg_ext = reg; + } } if (hub->port_off_mask && !hub->regmap) @@ -334,6 +359,12 @@ static int usb3503_probe(struct usb3503 *hub) } } + err = usb3503_ext_regulator(hub, 1); + if (err) { + dev_err(dev, "unable to enable ext regulator (%d)\n", + err); + } + usb3503_switch_mode(hub, hub->mode); dev_info(dev, "%s: probed in %s mode\n", __func__, @@ -364,6 +395,18 @@ static int usb3503_i2c_probe(struct i2c_client *i2c, return usb3503_probe(hub); } +static void usb3503_i2c_shutdown(struct i2c_client *i2c) +{ + struct usb3503 *hub; + int err; + + hub = i2c_get_clientdata(i2c); + if (hub) { + err = usb3503_ext_regulator(hub, 0); + BUG_ON(err); + } +} + static int usb3503_platform_probe(struct platform_device *pdev) { struct usb3503 *hub; @@ -376,6 +419,18 @@ static int usb3503_platform_probe(struct platform_device *pdev) return usb3503_probe(hub); } +static void usb3503_platform_shutdown(struct platform_device *pdev) +{ + struct usb3503 *hub; + int err; + + hub = platform_get_drvdata(pdev); + if (hub) { + err = usb3503_ext_regulator(hub, 0); + BUG_ON(err); + } +} + #ifdef CONFIG_PM_SLEEP static int usb3503_i2c_suspend(struct device *dev) { @@ -429,6 +484,7 @@ static struct i2c_driver usb3503_i2c_driver = { .of_match_table = of_match_ptr(usb3503_of_match), }, .probe = usb3503_i2c_probe, + .shutdown = usb3503_i2c_shutdown, .id_table = usb3503_id, }; @@ -438,6 +494,7 @@ static struct platform_driver usb3503_platform_driver = { .of_match_table = of_match_ptr(usb3503_of_match), }, .probe = usb3503_platform_probe, + .shutdown = usb3503_platform_shutdown, }; static int __init usb3503_init(void) From 99bb1fa70f6d5e0ddf9a4e731726afc18d877d8e Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sat, 19 Jul 2014 23:05:56 +0200 Subject: [PATCH 567/788] clk: exynos4: add APLL values for exynos4412 prime frequencies --- drivers/clk/samsung/clk-exynos4.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index 88e8c6bbd77ff8..5494781bd07f3b 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -1293,6 +1293,9 @@ static struct samsung_pll_rate_table exynos4210_vpll_rates[] __initdata = { }; static struct samsung_pll_rate_table exynos4x12_apll_rates[] __initdata = { + PLL_35XX_RATE(1800000000, 300, 4, 0), + PLL_35XX_RATE(1704000000, 213, 3, 0), + PLL_35XX_RATE(1600000000, 200, 3, 0), PLL_35XX_RATE(1500000000, 250, 4, 0), PLL_35XX_RATE(1400000000, 175, 3, 0), PLL_35XX_RATE(1300000000, 325, 6, 0), From 21724300a32c058f20edde35bf6bdc050e2c4fb4 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sat, 14 Jun 2014 23:45:54 +0200 Subject: [PATCH 568/788] drm/exynos: remove unused/deprecated DT binding documentation --- .../devicetree/bindings/video/exynos_hdmiddc.txt | 15 --------------- .../devicetree/bindings/video/exynos_hdmiphy.txt | 15 --------------- 2 files changed, 30 deletions(-) delete mode 100644 Documentation/devicetree/bindings/video/exynos_hdmiddc.txt delete mode 100644 Documentation/devicetree/bindings/video/exynos_hdmiphy.txt diff --git a/Documentation/devicetree/bindings/video/exynos_hdmiddc.txt b/Documentation/devicetree/bindings/video/exynos_hdmiddc.txt deleted file mode 100644 index 41eee971562b38..00000000000000 --- a/Documentation/devicetree/bindings/video/exynos_hdmiddc.txt +++ /dev/null @@ -1,15 +0,0 @@ -Device-Tree bindings for hdmiddc driver - -Required properties: -- compatible: value should be one of the following - 1) "samsung,exynos5-hdmiddc" - 2) "samsung,exynos4210-hdmiddc" - -- reg: I2C address of the hdmiddc device. - -Example: - - hdmiddc { - compatible = "samsung,exynos4210-hdmiddc"; - reg = <0x50>; - }; diff --git a/Documentation/devicetree/bindings/video/exynos_hdmiphy.txt b/Documentation/devicetree/bindings/video/exynos_hdmiphy.txt deleted file mode 100644 index 162f641f7639c0..00000000000000 --- a/Documentation/devicetree/bindings/video/exynos_hdmiphy.txt +++ /dev/null @@ -1,15 +0,0 @@ -Device-Tree bindings for hdmiphy driver - -Required properties: -- compatible: value should be one of the following: - 1) "samsung,exynos5-hdmiphy" - 2) "samsung,exynos4210-hdmiphy". - 3) "samsung,exynos4212-hdmiphy". -- reg: I2C address of the hdmiphy device. - -Example: - - hdmiphy { - compatible = "samsung,exynos4210-hdmiphy"; - reg = <0x38>; - }; From 5854b1b60559d1d361d2a130067caa558faeba27 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sun, 15 Jun 2014 03:49:23 +0200 Subject: [PATCH 569/788] clk: samsung: exynos4: Add gate clock for SSS module --- drivers/clk/samsung/clk-exynos4.c | 1 + include/dt-bindings/clock/exynos4.h | 3 +++ 2 files changed, 4 insertions(+) diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index 5494781bd07f3b..cb7dd07aaca322 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -1025,6 +1025,7 @@ static struct samsung_gate_clock exynos4_gate_clks[] __initdata = { GATE(CLK_PPMUDMC1, "ppmudmc1", "aclk133", GATE_IP_DMC, 9, 0, 0), GATE(CLK_PPMUCPU, "ppmucpu", "aclk133", GATE_IP_DMC, 10, 0, 0), GATE(CLK_PPMUACP, "ppmuacp", "aclk133", GATE_IP_DMC, 16, 0, 0), + GATE(CLK_SSS, "sss", "aclk133", GATE_IP_DMC, 4, 0, 0), GATE(CLK_OUT_LEFTBUS, "clkout_leftbus", "div_clkout_leftbus", CLKOUT_CMU_LEFTBUS, 16, CLK_SET_RATE_PARENT, 0), diff --git a/include/dt-bindings/clock/exynos4.h b/include/dt-bindings/clock/exynos4.h index 34fe28c622d0a6..6193f6c6ac343c 100644 --- a/include/dt-bindings/clock/exynos4.h +++ b/include/dt-bindings/clock/exynos4.h @@ -255,6 +255,9 @@ #define CLK_PPMUCPU 414 #define CLK_PPMUACP 415 +/* gate clocks - security sub system */ +#define CLK_SSS 416 + /* div clocks */ #define CLK_DIV_ISP0 450 /* Exynos4x12 only */ #define CLK_DIV_ISP1 451 /* Exynos4x12 only */ From fe6b7f18e852a875dce62b599c0864c634c7de03 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sun, 15 Jun 2014 03:01:05 +0200 Subject: [PATCH 570/788] ARM: dts: exynos4: add crypt-sss node --- arch/arm/boot/dts/exynos4.dtsi | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi index 24ff27049ce015..f366055f688817 100644 --- a/arch/arm/boot/dts/exynos4.dtsi +++ b/arch/arm/boot/dts/exynos4.dtsi @@ -645,4 +645,12 @@ samsung,sysreg = <&sys_reg>; status = "disabled"; }; + + sss@10830000 { + compatible = "samsung,exynos4210-secss"; + reg = <0x10830000 0x10000>; + interrupts = <0 112 0>; + clocks = <&clock CLK_SSS>; + clock-names = "secss"; + }; }; From 1c7102642fe1b0777c70dcfbcdc0e68a218e4f96 Mon Sep 17 00:00:00 2001 From: Kamil Debski Date: Tue, 4 Nov 2014 11:23:41 +0100 Subject: [PATCH 571/788] ARM: dts: Add pwm-fan node to the Odroid-U3 board Add pwm-fan node to the Odroid-U3 board file to enable PWM control of the cooling fan. In addition, add the "pwm" label to the pwm@139D0000 node in the exynos4412.dtsi. Signed-off-by: Kamil Debski --- arch/arm/boot/dts/exynos4.dtsi | 2 +- arch/arm/boot/dts/exynos4412-odroidu3.dts | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi index f366055f688817..324b10c0af4891 100644 --- a/arch/arm/boot/dts/exynos4.dtsi +++ b/arch/arm/boot/dts/exynos4.dtsi @@ -582,7 +582,7 @@ status = "disabled"; }; - pwm@139D0000 { + pwm: pwm@139D0000 { compatible = "samsung,exynos4210-pwm"; reg = <0x139D0000 0x1000>; interrupts = <0 37 0>, <0 38 0>, <0 39 0>, <0 40 0>, <0 41 0>; diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts b/arch/arm/boot/dts/exynos4412-odroidu3.dts index c8a64be55d071c..973349b9c413b1 100644 --- a/arch/arm/boot/dts/exynos4412-odroidu3.dts +++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts @@ -31,8 +31,22 @@ linux,default-trigger = "heartbeat"; }; }; + + pwm-fan { + compatible = "pwm-fan"; + pwms = <&pwm 0 10000 0>; + status = "okay"; + }; }; +&pwm { + pinctrl-0 = <&pwm0_out>; + pinctrl-names = "default"; + samsung,pwm-outputs = <0>; + status = "okay"; +}; + + &usb3503 { clock-names = "refclk"; clocks = <&pmu_system_controller 0>; From ca004dd34fd6e39a0d98f7c698cadf4893396e7b Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 31 Oct 2014 14:34:16 +0100 Subject: [PATCH 572/788] ARM: dts: Fix CLK_UART_ISP_SCLK clock assignment in exynos4x12.dtsi Assign proper FIMC-IS UART gate clock in the device DT node and not use the SRC_MASK gate. This fixes regression introduced in commit a37c82a3b3c0910019abfd22a97be1f ("clk: samsung: exynos4: Remove SRC_MASK_ISP gates"). Without this change exynos4 fimc-is driver fails to probe with an error log: [ 1.842447] ERROR: could not get clock /camera/fimc-is@12000000:uart(13) [ 1.848529] exynos4-fimc-is 12000000.fimc-is: failed to get clock: uart [ 1.855275] exynos4-fimc-is: probe of 12000000.fimc-is failed with error -2 Signed-off-by: Sylwester Nawrocki --- arch/arm/boot/dts/exynos4x12.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/exynos4x12.dtsi b/arch/arm/boot/dts/exynos4x12.dtsi index 93b70402e9439a..86e18004a6160e 100644 --- a/arch/arm/boot/dts/exynos4x12.dtsi +++ b/arch/arm/boot/dts/exynos4x12.dtsi @@ -225,7 +225,7 @@ <&clock CLK_DIV_ISP0>,<&clock CLK_DIV_ISP1>, <&clock CLK_DIV_MCUISP0>, <&clock CLK_DIV_MCUISP1>, - <&clock CLK_SCLK_UART_ISP>, + <&clock CLK_UART_ISP_SCLK>, <&clock CLK_ACLK200>, <&clock CLK_DIV_ACLK200>, <&clock CLK_ACLK400_MCUISP>, <&clock CLK_DIV_ACLK400_MCUISP>; From bf52ca7d6ff210a690d0e08f1097225822abb7ba Mon Sep 17 00:00:00 2001 From: Tomasz Stanislawski Date: Wed, 16 Apr 2014 17:12:56 +0200 Subject: [PATCH 573/788] drm: exynos: mixer: fix using usleep() in atomic context This patch fixes calling usleep_range() after taking reg_slock using spin_lock_irqsave(). The mdelay() is used instead. Waiting in atomic context is not the best idea in general. Hopefully, waiting occurs only when Video Processor fails to reset correctly. Signed-off-by: Tomasz Stanislawski --- drivers/gpu/drm/exynos/exynos_mixer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index 064ed6597defef..bb403e8c4cbb6f 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -636,7 +636,7 @@ static void vp_win_reset(struct mixer_context *ctx) /* waiting until VP_SRESET_PROCESSING is 0 */ if (~vp_reg_read(res, VP_SRESET) & VP_SRESET_PROCESSING) break; - usleep_range(10000, 12000); + mdelay(10); } WARN(tries == 0, "failed to reset Video Processor\n"); } From ed7593080bab369aee7b480b55212e5deef69cef Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 7 Jan 2015 12:30:16 +0100 Subject: [PATCH 574/788] ARM: OMAP2+: use common l2cache initialization code This patch implements generic DT L2C initialisation (the one from init_IRQ in arch/arm/kernel/irq.c) for Omap4 and AM43 platforms and kills the SoC specific stuff in arch/arm/mach-omap2/omap4-common.c. Signed-off-by: Marek Szyprowski Tested-by: Nishanth Menon Acked-by: Nishanth Menon Acked-by: Tony Lindgren --- arch/arm/mach-omap2/board-generic.c | 6 ++++++ arch/arm/mach-omap2/common.h | 8 ++++++++ arch/arm/mach-omap2/omap4-common.c | 16 +--------------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/arch/arm/mach-omap2/board-generic.c b/arch/arm/mach-omap2/board-generic.c index b61c049f92d6a3..42b7f4c9169b2a 100644 --- a/arch/arm/mach-omap2/board-generic.c +++ b/arch/arm/mach-omap2/board-generic.c @@ -189,6 +189,9 @@ static const char *const omap4_boards_compat[] __initconst = { }; DT_MACHINE_START(OMAP4_DT, "Generic OMAP4 (Flattened Device Tree)") + .l2c_aux_val = OMAP_L2C_AUX_CTRL, + .l2c_aux_mask = 0xcf9fffff, + .l2c_write_sec = omap4_l2c310_write_sec, .reserve = omap_reserve, .smp = smp_ops(omap4_smp_ops), .map_io = omap4_map_io, @@ -232,6 +235,9 @@ static const char *const am43_boards_compat[] __initconst = { }; DT_MACHINE_START(AM43_DT, "Generic AM43 (Flattened Device Tree)") + .l2c_aux_val = OMAP_L2C_AUX_CTRL, + .l2c_aux_mask = 0xcf9fffff, + .l2c_write_sec = omap4_l2c310_write_sec, .map_io = am33xx_map_io, .init_early = am43xx_init_early, .init_late = am43xx_init_late, diff --git a/arch/arm/mach-omap2/common.h b/arch/arm/mach-omap2/common.h index 64e44d6d07c0c8..3933b8aa4f0196 100644 --- a/arch/arm/mach-omap2/common.h +++ b/arch/arm/mach-omap2/common.h @@ -35,6 +35,7 @@ #include #include +#include #include "i2c.h" #include "serial.h" @@ -94,11 +95,18 @@ extern void omap3_gptimer_timer_init(void); extern void omap4_local_timer_init(void); #ifdef CONFIG_CACHE_L2X0 int omap_l2_cache_init(void); +#define OMAP_L2C_AUX_CTRL (L2C_AUX_CTRL_SHARED_OVERRIDE | \ + L310_AUX_CTRL_DATA_PREFETCH | \ + L310_AUX_CTRL_INSTR_PREFETCH) +void omap4_l2c310_write_sec(unsigned long val, unsigned reg); #else static inline int omap_l2_cache_init(void) { return 0; } + +#define OMAP_L2C_AUX_CTRL 0 +#define omap4_l2c310_write_sec NULL #endif extern void omap5_realtime_timer_init(void); diff --git a/arch/arm/mach-omap2/omap4-common.c b/arch/arm/mach-omap2/omap4-common.c index cc30e49a4cc278..2418bdf28ca271 100644 --- a/arch/arm/mach-omap2/omap4-common.c +++ b/arch/arm/mach-omap2/omap4-common.c @@ -166,7 +166,7 @@ void __iomem *omap4_get_l2cache_base(void) return l2cache_base; } -static void omap4_l2c310_write_sec(unsigned long val, unsigned reg) +void omap4_l2c310_write_sec(unsigned long val, unsigned reg) { unsigned smc_op; @@ -201,24 +201,10 @@ static void omap4_l2c310_write_sec(unsigned long val, unsigned reg) int __init omap_l2_cache_init(void) { - u32 aux_ctrl; - /* Static mapping, never released */ l2cache_base = ioremap(OMAP44XX_L2CACHE_BASE, SZ_4K); if (WARN_ON(!l2cache_base)) return -ENOMEM; - - /* 16-way associativity, parity disabled, way size - 64KB (es2.0 +) */ - aux_ctrl = L2C_AUX_CTRL_SHARED_OVERRIDE | - L310_AUX_CTRL_DATA_PREFETCH | - L310_AUX_CTRL_INSTR_PREFETCH; - - outer_cache.write_sec = omap4_l2c310_write_sec; - if (of_have_populated_dt()) - l2x0_of_init(aux_ctrl, 0xcf9fffff); - else - l2x0_init(l2cache_base, aux_ctrl, 0xcf9fffff); - return 0; } #endif From 490c88bc2215913d220dbcad9b0d768f5c61657f Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 7 Jan 2015 12:30:17 +0100 Subject: [PATCH 575/788] ARM: l2c: use l2c_write_sec() for restoring latency and filter regs All four register for latency and filter settings cannot be written in non-secure mode and they should go through l2c_write_sec(). More on this can be found in CoreLink Level 2 Cache Controller L2C-310 Technical Reference Manual, 3.2. Register summary, table 3.1. This have been checked the TRM for r3p3, but it should be uniform for all revisions. Reported-by: Nishanth Menon Suggested-by: Tomasz Figa Signed-off-by: Marek Szyprowski Tested-by: Nishanth Menon Acked-by: Nishanth Menon Acked-by: Tony Lindgren --- arch/arm/mm/cache-l2x0.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 5e65ca8dea62cf..b83c401ca50c4a 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -623,14 +623,14 @@ static void l2c310_resume(void) unsigned revision; /* restore pl310 setup */ - writel_relaxed(l2x0_saved_regs.tag_latency, - base + L310_TAG_LATENCY_CTRL); - writel_relaxed(l2x0_saved_regs.data_latency, - base + L310_DATA_LATENCY_CTRL); - writel_relaxed(l2x0_saved_regs.filter_end, - base + L310_ADDR_FILTER_END); - writel_relaxed(l2x0_saved_regs.filter_start, - base + L310_ADDR_FILTER_START); + l2c_write_sec(l2x0_saved_regs.tag_latency, base, + L310_TAG_LATENCY_CTRL); + l2c_write_sec(l2x0_saved_regs.data_latency, base, + L310_DATA_LATENCY_CTRL); + l2c_write_sec(l2x0_saved_regs.filter_end, base, + L310_ADDR_FILTER_END); + l2c_write_sec(l2x0_saved_regs.filter_start, base, + L310_ADDR_FILTER_START); revision = readl_relaxed(base + L2X0_CACHE_ID) & L2X0_CACHE_ID_RTL_MASK; @@ -1135,28 +1135,28 @@ static void __init l2c310_of_parse(const struct device_node *np, of_property_read_u32_array(np, "arm,tag-latency", tag, ARRAY_SIZE(tag)); if (tag[0] && tag[1] && tag[2]) - writel_relaxed( + l2c_write_sec( L310_LATENCY_CTRL_RD(tag[0] - 1) | L310_LATENCY_CTRL_WR(tag[1] - 1) | L310_LATENCY_CTRL_SETUP(tag[2] - 1), - l2x0_base + L310_TAG_LATENCY_CTRL); + l2x0_base, L310_TAG_LATENCY_CTRL); of_property_read_u32_array(np, "arm,data-latency", data, ARRAY_SIZE(data)); if (data[0] && data[1] && data[2]) - writel_relaxed( + l2c_write_sec( L310_LATENCY_CTRL_RD(data[0] - 1) | L310_LATENCY_CTRL_WR(data[1] - 1) | L310_LATENCY_CTRL_SETUP(data[2] - 1), - l2x0_base + L310_DATA_LATENCY_CTRL); + l2x0_base, L310_DATA_LATENCY_CTRL); of_property_read_u32_array(np, "arm,filter-ranges", filter, ARRAY_SIZE(filter)); if (filter[1]) { - writel_relaxed(ALIGN(filter[0] + filter[1], SZ_1M), - l2x0_base + L310_ADDR_FILTER_END); - writel_relaxed((filter[0] & ~(SZ_1M - 1)) | L310_ADDR_FILTER_EN, - l2x0_base + L310_ADDR_FILTER_START); + l2c_write_sec(ALIGN(filter[0] + filter[1], SZ_1M), + l2x0_base, L310_ADDR_FILTER_END); + l2c_write_sec((filter[0] & ~(SZ_1M - 1)) | L310_ADDR_FILTER_EN, + l2x0_base, L310_ADDR_FILTER_START); } ret = l2x0_cache_size_of_parse(np, aux_val, aux_mask, &assoc, SZ_512K); From ae2138be1c4d26b474fe6888d098b20a3895e042 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Wed, 7 Jan 2015 12:30:18 +0100 Subject: [PATCH 576/788] ARM: l2c: Refactor the driver to use commit-like interface Certain implementations of secure hypervisors (namely the one found on Samsung Exynos-based boards) do not provide access to individual L2C registers. This makes the .write_sec()-based interface insufficient and provoking ugly hacks. This patch is first step to make the driver not rely on availability of writes to individual registers. This is achieved by refactoring the driver to use a commit-like operation scheme: all register values are prepared first and stored in an instance of l2x0_regs struct and then a single callback is responsible to flush those values to the hardware. Signed-off-by: Tomasz Figa [mszyprow: rebased onto 'ARM: l2c: use l2c_write_sec() for restoring latency and filter regs' patch] Signed-off-by: Marek Szyprowski Tested-by: Nishanth Menon Acked-by: Tony Lindgren --- arch/arm/mm/cache-l2x0.c | 212 +++++++++++++++++++++------------------ 1 file changed, 116 insertions(+), 96 deletions(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index b83c401ca50c4a..dde0d54ac41e58 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -41,12 +41,14 @@ struct l2c_init_data { void (*enable)(void __iomem *, u32, unsigned); void (*fixup)(void __iomem *, u32, struct outer_cache_fns *); void (*save)(void __iomem *); + void (*configure)(void __iomem *); struct outer_cache_fns outer_cache; }; #define CACHE_LINE_SIZE 32 static void __iomem *l2x0_base; +static const struct l2c_init_data *l2x0_data; static DEFINE_RAW_SPINLOCK(l2x0_lock); static u32 l2x0_way_mask; /* Bitmask of active ways */ static u32 l2x0_size; @@ -106,6 +108,14 @@ static inline void l2c_unlock(void __iomem *base, unsigned num) } } +static void l2c_configure(void __iomem *base) +{ + if (l2x0_data->configure) + l2x0_data->configure(base); + + l2c_write_sec(l2x0_saved_regs.aux_ctrl, base, L2X0_AUX_CTRL); +} + /* * Enable the L2 cache controller. This function must only be * called when the cache controller is known to be disabled. @@ -114,7 +124,12 @@ static void l2c_enable(void __iomem *base, u32 aux, unsigned num_lock) { unsigned long flags; - l2c_write_sec(aux, base, L2X0_AUX_CTRL); + /* Do not touch the controller if already enabled. */ + if (readl_relaxed(base + L2X0_CTRL) & L2X0_CTRL_EN) + return; + + l2x0_saved_regs.aux_ctrl = aux; + l2c_configure(base); l2c_unlock(base, num_lock); @@ -208,6 +223,11 @@ static void l2c_save(void __iomem *base) l2x0_saved_regs.aux_ctrl = readl_relaxed(l2x0_base + L2X0_AUX_CTRL); } +static void l2c_resume(void) +{ + l2c_enable(l2x0_base, l2x0_saved_regs.aux_ctrl, l2x0_data->num_lock); +} + /* * L2C-210 specific code. * @@ -288,14 +308,6 @@ static void l2c210_sync(void) __l2c210_cache_sync(l2x0_base); } -static void l2c210_resume(void) -{ - void __iomem *base = l2x0_base; - - if (!(readl_relaxed(base + L2X0_CTRL) & L2X0_CTRL_EN)) - l2c_enable(base, l2x0_saved_regs.aux_ctrl, 1); -} - static const struct l2c_init_data l2c210_data __initconst = { .type = "L2C-210", .way_size_0 = SZ_8K, @@ -309,7 +321,7 @@ static const struct l2c_init_data l2c210_data __initconst = { .flush_all = l2c210_flush_all, .disable = l2c_disable, .sync = l2c210_sync, - .resume = l2c210_resume, + .resume = l2c_resume, }, }; @@ -466,7 +478,7 @@ static const struct l2c_init_data l2c220_data = { .flush_all = l2c220_flush_all, .disable = l2c_disable, .sync = l2c220_sync, - .resume = l2c210_resume, + .resume = l2c_resume, }, }; @@ -615,39 +627,29 @@ static void __init l2c310_save(void __iomem *base) L310_POWER_CTRL); } -static void l2c310_resume(void) +static void l2c310_configure(void __iomem *base) { - void __iomem *base = l2x0_base; + unsigned revision; - if (!(readl_relaxed(base + L2X0_CTRL) & L2X0_CTRL_EN)) { - unsigned revision; - - /* restore pl310 setup */ - l2c_write_sec(l2x0_saved_regs.tag_latency, base, - L310_TAG_LATENCY_CTRL); - l2c_write_sec(l2x0_saved_regs.data_latency, base, - L310_DATA_LATENCY_CTRL); - l2c_write_sec(l2x0_saved_regs.filter_end, base, - L310_ADDR_FILTER_END); - l2c_write_sec(l2x0_saved_regs.filter_start, base, - L310_ADDR_FILTER_START); - - revision = readl_relaxed(base + L2X0_CACHE_ID) & - L2X0_CACHE_ID_RTL_MASK; - - if (revision >= L310_CACHE_ID_RTL_R2P0) - l2c_write_sec(l2x0_saved_regs.prefetch_ctrl, base, - L310_PREFETCH_CTRL); - if (revision >= L310_CACHE_ID_RTL_R3P0) - l2c_write_sec(l2x0_saved_regs.pwr_ctrl, base, - L310_POWER_CTRL); - - l2c_enable(base, l2x0_saved_regs.aux_ctrl, 8); - - /* Re-enable full-line-of-zeros for Cortex-A9 */ - if (l2x0_saved_regs.aux_ctrl & L310_AUX_CTRL_FULL_LINE_ZERO) - set_auxcr(get_auxcr() | BIT(3) | BIT(2) | BIT(1)); - } + /* restore pl310 setup */ + l2c_write_sec(l2x0_saved_regs.tag_latency, base, + L310_TAG_LATENCY_CTRL); + l2c_write_sec(l2x0_saved_regs.data_latency, base, + L310_DATA_LATENCY_CTRL); + l2c_write_sec(l2x0_saved_regs.filter_end, base, + L310_ADDR_FILTER_END); + l2c_write_sec(l2x0_saved_regs.filter_start, base, + L310_ADDR_FILTER_START); + + revision = readl_relaxed(base + L2X0_CACHE_ID) & + L2X0_CACHE_ID_RTL_MASK; + + if (revision >= L310_CACHE_ID_RTL_R2P0) + l2c_write_sec(l2x0_saved_regs.prefetch_ctrl, base, + L310_PREFETCH_CTRL); + if (revision >= L310_CACHE_ID_RTL_R3P0) + l2c_write_sec(l2x0_saved_regs.pwr_ctrl, base, + L310_POWER_CTRL); } static int l2c310_cpu_enable_flz(struct notifier_block *nb, unsigned long act, void *data) @@ -699,6 +701,23 @@ static void __init l2c310_enable(void __iomem *base, u32 aux, unsigned num_lock) aux &= ~(L310_AUX_CTRL_FULL_LINE_ZERO | L310_AUX_CTRL_EARLY_BRESP); } + /* r3p0 or later has power control register */ + if (rev >= L310_CACHE_ID_RTL_R3P0) + l2x0_saved_regs.pwr_ctrl = L310_DYNAMIC_CLK_GATING_EN | + L310_STNDBY_MODE_EN; + + /* + * Always enable non-secure access to the lockdown registers - + * we write to them as part of the L2C enable sequence so they + * need to be accessible. + */ + aux |= L310_AUX_CTRL_NS_LOCKDOWN; + + l2c_enable(base, aux, num_lock); + + /* Read back resulting AUX_CTRL value as it could have been altered. */ + aux = readl_relaxed(base + L2X0_AUX_CTRL); + if (aux & (L310_AUX_CTRL_DATA_PREFETCH | L310_AUX_CTRL_INSTR_PREFETCH)) { u32 prefetch = readl_relaxed(base + L310_PREFETCH_CTRL); @@ -712,23 +731,12 @@ static void __init l2c310_enable(void __iomem *base, u32 aux, unsigned num_lock) if (rev >= L310_CACHE_ID_RTL_R3P0) { u32 power_ctrl; - l2c_write_sec(L310_DYNAMIC_CLK_GATING_EN | L310_STNDBY_MODE_EN, - base, L310_POWER_CTRL); power_ctrl = readl_relaxed(base + L310_POWER_CTRL); pr_info("L2C-310 dynamic clock gating %sabled, standby mode %sabled\n", power_ctrl & L310_DYNAMIC_CLK_GATING_EN ? "en" : "dis", power_ctrl & L310_STNDBY_MODE_EN ? "en" : "dis"); } - /* - * Always enable non-secure access to the lockdown registers - - * we write to them as part of the L2C enable sequence so they - * need to be accessible. - */ - aux |= L310_AUX_CTRL_NS_LOCKDOWN; - - l2c_enable(base, aux, num_lock); - if (aux & L310_AUX_CTRL_FULL_LINE_ZERO) { set_auxcr(get_auxcr() | BIT(3) | BIT(2) | BIT(1)); cpu_notifier(l2c310_cpu_enable_flz, 0); @@ -760,11 +768,11 @@ static void __init l2c310_fixup(void __iomem *base, u32 cache_id, if (revision >= L310_CACHE_ID_RTL_R3P0 && revision < L310_CACHE_ID_RTL_R3P2) { - u32 val = readl_relaxed(base + L310_PREFETCH_CTRL); + u32 val = l2x0_saved_regs.prefetch_ctrl; /* I don't think bit23 is required here... but iMX6 does so */ if (val & (BIT(30) | BIT(23))) { val &= ~(BIT(30) | BIT(23)); - l2c_write_sec(val, base, L310_PREFETCH_CTRL); + l2x0_saved_regs.prefetch_ctrl = val; errata[n++] = "752271"; } } @@ -800,6 +808,15 @@ static void l2c310_disable(void) l2c_disable(); } +static void l2c310_resume(void) +{ + l2c_resume(); + + /* Re-enable full-line-of-zeros for Cortex-A9 */ + if (l2x0_saved_regs.aux_ctrl & L310_AUX_CTRL_FULL_LINE_ZERO) + set_auxcr(get_auxcr() | BIT(3) | BIT(2) | BIT(1)); +} + static const struct l2c_init_data l2c310_init_fns __initconst = { .type = "L2C-310", .way_size_0 = SZ_8K, @@ -807,6 +824,7 @@ static const struct l2c_init_data l2c310_init_fns __initconst = { .enable = l2c310_enable, .fixup = l2c310_fixup, .save = l2c310_save, + .configure = l2c310_configure, .outer_cache = { .inv_range = l2c210_inv_range, .clean_range = l2c210_clean_range, @@ -818,13 +836,21 @@ static const struct l2c_init_data l2c310_init_fns __initconst = { }, }; -static void __init __l2c_init(const struct l2c_init_data *data, - u32 aux_val, u32 aux_mask, u32 cache_id) +static int __init __l2c_init(const struct l2c_init_data *data, + u32 aux_val, u32 aux_mask, u32 cache_id) { struct outer_cache_fns fns; unsigned way_size_bits, ways; u32 aux, old_aux; + /* + * Save the pointer globally so that callbacks which do not receive + * context from callers can access the structure. + */ + l2x0_data = kmemdup(data, sizeof(*data), GFP_KERNEL); + if (!l2x0_data) + return -ENOMEM; + /* * Sanity check the aux values. aux_mask is the bits we preserve * from reading the hardware register, and aux_val is the bits we @@ -910,6 +936,8 @@ static void __init __l2c_init(const struct l2c_init_data *data, data->type, ways, l2x0_size >> 10); pr_info("%s: CACHE_ID 0x%08x, AUX_CTRL 0x%08x\n", data->type, cache_id, aux); + + return 0; } void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) @@ -936,6 +964,10 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) break; } + /* Read back current (default) hardware configuration */ + if (data->save) + data->save(l2x0_base); + __l2c_init(data, aux_val, aux_mask, cache_id); } @@ -1102,7 +1134,7 @@ static const struct l2c_init_data of_l2c210_data __initconst = { .flush_all = l2c210_flush_all, .disable = l2c_disable, .sync = l2c210_sync, - .resume = l2c210_resume, + .resume = l2c_resume, }, }; @@ -1120,7 +1152,7 @@ static const struct l2c_init_data of_l2c220_data __initconst = { .flush_all = l2c220_flush_all, .disable = l2c_disable, .sync = l2c220_sync, - .resume = l2c210_resume, + .resume = l2c_resume, }, }; @@ -1135,28 +1167,26 @@ static void __init l2c310_of_parse(const struct device_node *np, of_property_read_u32_array(np, "arm,tag-latency", tag, ARRAY_SIZE(tag)); if (tag[0] && tag[1] && tag[2]) - l2c_write_sec( + l2x0_saved_regs.tag_latency = L310_LATENCY_CTRL_RD(tag[0] - 1) | L310_LATENCY_CTRL_WR(tag[1] - 1) | - L310_LATENCY_CTRL_SETUP(tag[2] - 1), - l2x0_base, L310_TAG_LATENCY_CTRL); + L310_LATENCY_CTRL_SETUP(tag[2] - 1); of_property_read_u32_array(np, "arm,data-latency", data, ARRAY_SIZE(data)); if (data[0] && data[1] && data[2]) - l2c_write_sec( + l2x0_saved_regs.data_latency = L310_LATENCY_CTRL_RD(data[0] - 1) | L310_LATENCY_CTRL_WR(data[1] - 1) | - L310_LATENCY_CTRL_SETUP(data[2] - 1), - l2x0_base, L310_DATA_LATENCY_CTRL); + L310_LATENCY_CTRL_SETUP(data[2] - 1); of_property_read_u32_array(np, "arm,filter-ranges", filter, ARRAY_SIZE(filter)); if (filter[1]) { - l2c_write_sec(ALIGN(filter[0] + filter[1], SZ_1M), - l2x0_base, L310_ADDR_FILTER_END); - l2c_write_sec((filter[0] & ~(SZ_1M - 1)) | L310_ADDR_FILTER_EN, - l2x0_base, L310_ADDR_FILTER_START); + l2x0_saved_regs.filter_end = + ALIGN(filter[0] + filter[1], SZ_1M); + l2x0_saved_regs.filter_start = (filter[0] & ~(SZ_1M - 1)) + | L310_ADDR_FILTER_EN; } ret = l2x0_cache_size_of_parse(np, aux_val, aux_mask, &assoc, SZ_512K); @@ -1188,6 +1218,7 @@ static const struct l2c_init_data of_l2c310_data __initconst = { .enable = l2c310_enable, .fixup = l2c310_fixup, .save = l2c310_save, + .configure = l2c310_configure, .outer_cache = { .inv_range = l2c210_inv_range, .clean_range = l2c210_clean_range, @@ -1216,6 +1247,7 @@ static const struct l2c_init_data of_l2c310_coherent_data __initconst = { .enable = l2c310_enable, .fixup = l2c310_fixup, .save = l2c310_save, + .configure = l2c310_configure, .outer_cache = { .inv_range = l2c210_inv_range, .clean_range = l2c210_clean_range, @@ -1330,16 +1362,6 @@ static void aurora_save(void __iomem *base) l2x0_saved_regs.aux_ctrl = readl_relaxed(base + L2X0_AUX_CTRL); } -static void aurora_resume(void) -{ - void __iomem *base = l2x0_base; - - if (!(readl(base + L2X0_CTRL) & L2X0_CTRL_EN)) { - writel_relaxed(l2x0_saved_regs.aux_ctrl, base + L2X0_AUX_CTRL); - writel_relaxed(l2x0_saved_regs.ctrl, base + L2X0_CTRL); - } -} - /* * For Aurora cache in no outer mode, enable via the CP15 coprocessor * broadcasting of cache commands to L2. @@ -1401,7 +1423,7 @@ static const struct l2c_init_data of_aurora_with_outer_data __initconst = { .flush_all = l2x0_flush_all, .disable = l2x0_disable, .sync = l2x0_cache_sync, - .resume = aurora_resume, + .resume = l2c_resume, }, }; @@ -1414,7 +1436,7 @@ static const struct l2c_init_data of_aurora_no_outer_data __initconst = { .fixup = aurora_fixup, .save = aurora_save, .outer_cache = { - .resume = aurora_resume, + .resume = l2c_resume, }, }; @@ -1562,6 +1584,7 @@ static const struct l2c_init_data of_bcm_l2x0_data __initconst = { .of_parse = l2c310_of_parse, .enable = l2c310_enable, .save = l2c310_save, + .configure = l2c310_configure, .outer_cache = { .inv_range = bcm_inv_range, .clean_range = bcm_clean_range, @@ -1583,18 +1606,12 @@ static void __init tauros3_save(void __iomem *base) readl_relaxed(base + L310_PREFETCH_CTRL); } -static void tauros3_resume(void) +static void tauros3_configure(void __iomem *base) { - void __iomem *base = l2x0_base; - - if (!(readl_relaxed(base + L2X0_CTRL) & L2X0_CTRL_EN)) { - writel_relaxed(l2x0_saved_regs.aux2_ctrl, - base + TAUROS3_AUX2_CTRL); - writel_relaxed(l2x0_saved_regs.prefetch_ctrl, - base + L310_PREFETCH_CTRL); - - l2c_enable(base, l2x0_saved_regs.aux_ctrl, 8); - } + writel_relaxed(l2x0_saved_regs.aux2_ctrl, + base + TAUROS3_AUX2_CTRL); + writel_relaxed(l2x0_saved_regs.prefetch_ctrl, + base + L310_PREFETCH_CTRL); } static const struct l2c_init_data of_tauros3_data __initconst = { @@ -1603,9 +1620,10 @@ static const struct l2c_init_data of_tauros3_data __initconst = { .num_lock = 8, .enable = l2c_enable, .save = tauros3_save, + .configure = tauros3_configure, /* Tauros3 broadcasts L1 cache operations to L2 */ .outer_cache = { - .resume = tauros3_resume, + .resume = l2c_resume, }, }; @@ -1661,6 +1679,10 @@ int __init l2x0_of_init(u32 aux_val, u32 aux_mask) if (!of_property_read_bool(np, "cache-unified")) pr_err("L2C: device tree omits to specify unified cache\n"); + /* Read back current (default) hardware configuration */ + if (data->save) + data->save(l2x0_base); + /* L2 configuration can only be changed if the cache is disabled */ if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) if (data->of_parse) @@ -1671,8 +1693,6 @@ int __init l2x0_of_init(u32 aux_val, u32 aux_mask) else cache_id = readl_relaxed(l2x0_base + L2X0_CACHE_ID); - __l2c_init(data, aux_val, aux_mask, cache_id); - - return 0; + return __l2c_init(data, aux_val, aux_mask, cache_id); } #endif From 8d72d6bd2d112e404dc88f0d29c859d69a4e34a4 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Wed, 7 Jan 2015 12:30:19 +0100 Subject: [PATCH 577/788] ARM: l2c: Add interface to ask hypervisor to configure L2C Because certain secure hypervisor do not allow writes to individual L2C registers, but rather expect set of parameters to be passed as argument to secure monitor calls, there is a need to provide an interface for the L2C driver to ask the firmware to configure the hardware according to specified parameters. This patch adds such. Signed-off-by: Tomasz Figa Signed-off-by: Marek Szyprowski Tested-by: Nishanth Menon Acked-by: Nishanth Menon Acked-by: Tony Lindgren --- arch/arm/include/asm/outercache.h | 3 +++ arch/arm/mm/cache-l2x0.c | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/arch/arm/include/asm/outercache.h b/arch/arm/include/asm/outercache.h index 891a56b35bcf0c..563b92fc2f41c3 100644 --- a/arch/arm/include/asm/outercache.h +++ b/arch/arm/include/asm/outercache.h @@ -23,6 +23,8 @@ #include +struct l2x0_regs; + struct outer_cache_fns { void (*inv_range)(unsigned long, unsigned long); void (*clean_range)(unsigned long, unsigned long); @@ -36,6 +38,7 @@ struct outer_cache_fns { /* This is an ARM L2C thing */ void (*write_sec)(unsigned long, unsigned); + void (*configure)(const struct l2x0_regs *); }; extern struct outer_cache_fns outer_cache; diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index dde0d54ac41e58..5288153f28b889 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -110,6 +110,11 @@ static inline void l2c_unlock(void __iomem *base, unsigned num) static void l2c_configure(void __iomem *base) { + if (outer_cache.configure) { + outer_cache.configure(&l2x0_saved_regs); + return; + } + if (l2x0_data->configure) l2x0_data->configure(base); @@ -910,6 +915,7 @@ static int __init __l2c_init(const struct l2c_init_data *data, fns = data->outer_cache; fns.write_sec = outer_cache.write_sec; + fns.configure = outer_cache.configure; if (data->fixup) data->fixup(l2x0_base, cache_id, &fns); From c5c871b0051f8cea4b6782ef6746dc5794741a4c Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Wed, 7 Jan 2015 12:30:20 +0100 Subject: [PATCH 578/788] ARM: l2c: Get outer cache .write_sec callback from mach_desc only if not NULL Certain platforms (i.e. Exynos) might need to set .write_sec callback from firmware initialization which is happenning in .init_early callback of machine descriptor. However current code will overwrite the pointer with whatever is present in machine descriptor, even though it can be already set earlier. This patch fixes this by making the assignment conditional, depending on whether current .write_sec callback is NULL. Signed-off-by: Tomasz Figa Signed-off-by: Marek Szyprowski Tested-by: Nishanth Menon Acked-by: Nishanth Menon Acked-by: Tony Lindgren --- arch/arm/kernel/irq.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c index ad857bada96ce7..350f188c92d294 100644 --- a/arch/arm/kernel/irq.c +++ b/arch/arm/kernel/irq.c @@ -109,7 +109,8 @@ void __init init_IRQ(void) if (IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_CACHE_L2X0) && (machine_desc->l2c_aux_mask || machine_desc->l2c_aux_val)) { - outer_cache.write_sec = machine_desc->l2c_write_sec; + if (!outer_cache.write_sec) + outer_cache.write_sec = machine_desc->l2c_write_sec; ret = l2x0_of_init(machine_desc->l2c_aux_val, machine_desc->l2c_aux_mask); if (ret) From 769bf4305a1f875b23187a61dcec8ea0f849cd27 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Wed, 7 Jan 2015 12:30:21 +0100 Subject: [PATCH 579/788] ARM: l2c: Add support for overriding prefetch settings Firmware on certain boards (e.g. ODROID-U3) can leave incorrect L2C prefetch settings configured in registers leading to crashes if L2C is enabled without overriding them. This patch introduces bindings to enable prefetch settings to be specified from DT and necessary support in the driver. Signed-off-by: Tomasz Figa [mszyprow: rebased onto v3.18-rc1, added error message when prefetch related dt property has been provided without any value] Signed-off-by: Marek Szyprowski Tested-by: Nishanth Menon Acked-by: Nishanth Menon Acked-by: Tony Lindgren --- .../devicetree/bindings/arm/l2cc.txt | 10 ++++ arch/arm/mm/cache-l2x0.c | 54 +++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/l2cc.txt b/Documentation/devicetree/bindings/arm/l2cc.txt index 292ef7ca305853..0dbabe9a6b0abb 100644 --- a/Documentation/devicetree/bindings/arm/l2cc.txt +++ b/Documentation/devicetree/bindings/arm/l2cc.txt @@ -57,6 +57,16 @@ Optional properties: - cache-id-part: cache id part number to be used if it is not present on hardware - wt-override: If present then L2 is forced to Write through mode +- arm,double-linefill : Override double linefill enable setting. Enable if + non-zero, disable if zero. +- arm,double-linefill-incr : Override double linefill on INCR read. Enable + if non-zero, disable if zero. +- arm,double-linefill-wrap : Override double linefill on WRAP read. Enable + if non-zero, disable if zero. +- arm,prefetch-drop : Override prefetch drop enable setting. Enable if non-zero, + disable if zero. +- arm,prefetch-offset : Override prefetch offset value. Valid values are + 0-7, 15, 23, and 31. Example: diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 5288153f28b889..01de138094548b 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -1169,6 +1169,8 @@ static void __init l2c310_of_parse(const struct device_node *np, u32 tag[3] = { 0, 0, 0 }; u32 filter[2] = { 0, 0 }; u32 assoc; + u32 prefetch; + u32 val; int ret; of_property_read_u32_array(np, "arm,tag-latency", tag, ARRAY_SIZE(tag)); @@ -1214,6 +1216,58 @@ static void __init l2c310_of_parse(const struct device_node *np, assoc); break; } + + prefetch = l2x0_saved_regs.prefetch_ctrl; + + ret = of_property_read_u32(np, "arm,double-linefill", &val); + if (ret == 0) { + if (val) + prefetch |= L310_PREFETCH_CTRL_DBL_LINEFILL; + else + prefetch &= ~L310_PREFETCH_CTRL_DBL_LINEFILL; + } else if (ret != -EINVAL) { + pr_err("L2C-310 OF arm,double-linefill property value is missing\n"); + } + + ret = of_property_read_u32(np, "arm,double-linefill-incr", &val); + if (ret == 0) { + if (val) + prefetch |= L310_PREFETCH_CTRL_DBL_LINEFILL_INCR; + else + prefetch &= ~L310_PREFETCH_CTRL_DBL_LINEFILL_INCR; + } else if (ret != -EINVAL) { + pr_err("L2C-310 OF arm,double-linefill-incr property value is missing\n"); + } + + ret = of_property_read_u32(np, "arm,double-linefill-wrap", &val); + if (ret == 0) { + if (!val) + prefetch |= L310_PREFETCH_CTRL_DBL_LINEFILL_WRAP; + else + prefetch &= ~L310_PREFETCH_CTRL_DBL_LINEFILL_WRAP; + } else if (ret != -EINVAL) { + pr_err("L2C-310 OF arm,double-linefill-wrap property value is missing\n"); + } + + ret = of_property_read_u32(np, "arm,prefetch-drop", &val); + if (ret == 0) { + if (val) + prefetch |= L310_PREFETCH_CTRL_PREFETCH_DROP; + else + prefetch &= ~L310_PREFETCH_CTRL_PREFETCH_DROP; + } else if (ret != -EINVAL) { + pr_err("L2C-310 OF arm,prefetch-drop property value is missing\n"); + } + + ret = of_property_read_u32(np, "arm,prefetch-offset", &val); + if (ret == 0) { + prefetch &= ~L310_PREFETCH_CTRL_OFFSET_MASK; + prefetch |= val & L310_PREFETCH_CTRL_OFFSET_MASK; + } else if (ret != -EINVAL) { + pr_err("L2C-310 OF arm,prefetch-offset property value is missing\n"); + } + + l2x0_saved_regs.prefetch_ctrl = prefetch; } static const struct l2c_init_data of_l2c310_data __initconst = { From 98e2ccaf0a4a5efc6f35305bb8f649046ff497ae Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Wed, 7 Jan 2015 12:30:22 +0100 Subject: [PATCH 580/788] ARM: EXYNOS: Add .write_sec outer cache callback for L2C-310 Exynos4 SoCs equipped with an L2C-310 cache controller and running under secure firmware require certain registers of aforementioned IP to be accessed only from secure mode. This means that SMC calls are required for certain register writes. To handle this, an implementation of .write_sec and .configure callbacks is provided by this patch. Signed-off-by: Tomasz Figa [added comment and reworked unconditional call to SMC_CMD_L2X0INVALL] Signed-off-by: Marek Szyprowski Acked-by: Arnd Bergmann Acked-by: Kukjin Kim --- arch/arm/mach-exynos/firmware.c | 50 +++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/arch/arm/mach-exynos/firmware.c b/arch/arm/mach-exynos/firmware.c index 766f57d2f029ac..e95cab6ba4788f 100644 --- a/arch/arm/mach-exynos/firmware.c +++ b/arch/arm/mach-exynos/firmware.c @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -136,6 +137,43 @@ static const struct firmware_ops exynos_firmware_ops = { .resume = IS_ENABLED(CONFIG_EXYNOS_CPU_SUSPEND) ? exynos_resume : NULL, }; +static void exynos_l2_write_sec(unsigned long val, unsigned reg) +{ + static int l2cache_enabled; + + switch (reg) { + case L2X0_CTRL: + if (val & L2X0_CTRL_EN) { + /* + * Before the cache can be enabled, due to firmware + * design, SMC_CMD_L2X0INVALL must be called. + */ + if (!l2cache_enabled) { + exynos_smc(SMC_CMD_L2X0INVALL, 0, 0, 0); + l2cache_enabled = 1; + } + } else { + l2cache_enabled = 0; + } + exynos_smc(SMC_CMD_L2X0CTRL, val, 0, 0); + break; + + case L2X0_DEBUG_CTRL: + exynos_smc(SMC_CMD_L2X0DEBUG, val, 0, 0); + break; + + default: + WARN_ONCE(1, "%s: ignoring write to reg 0x%x\n", __func__, reg); + } +} + +static void exynos_l2_configure(const struct l2x0_regs *regs) +{ + exynos_smc(SMC_CMD_L2X0SETUP1, regs->tag_latency, regs->data_latency, + regs->prefetch_ctrl); + exynos_smc(SMC_CMD_L2X0SETUP2, regs->pwr_ctrl, regs->aux_ctrl, 0); +} + void __init exynos_firmware_init(void) { struct device_node *nd; @@ -155,4 +193,16 @@ void __init exynos_firmware_init(void) pr_info("Running under secure firmware.\n"); register_firmware_ops(&exynos_firmware_ops); + + /* + * Exynos 4 SoCs (based on Cortex A9 and equipped with L2C-310), + * running under secure firmware, require certain registers of L2 + * cache controller to be written in secure mode. Here .write_sec + * callback is provided to perform necessary SMC calls. + */ + if (IS_ENABLED(CONFIG_CACHE_L2X0) && + read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) { + outer_cache.write_sec = exynos_l2_write_sec; + outer_cache.configure = exynos_l2_configure; + } } From d6b5435ca41bb7d2aa85916b5c91f2993eb453c6 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Wed, 7 Jan 2015 12:30:23 +0100 Subject: [PATCH 581/788] ARM: EXYNOS: Add support for non-secure L2X0 resume On Exynos SoCs it is necessary to resume operation of L2C early in assembly code, because otherwise certain systems will crash. This patch adds necessary code to non-secure resume handler. Signed-off-by: Tomasz Figa [rewrote the code accessing l2x0_saved_regs] Signed-off-by: Marek Szyprowski Acked-by: Arnd Bergmann Acked-by: Kukjin Kim --- arch/arm/mach-exynos/sleep.S | 46 ++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/arch/arm/mach-exynos/sleep.S b/arch/arm/mach-exynos/sleep.S index e3c373082bbee3..31d25834b9c4b0 100644 --- a/arch/arm/mach-exynos/sleep.S +++ b/arch/arm/mach-exynos/sleep.S @@ -16,6 +16,8 @@ */ #include +#include +#include #include "smc.h" #define CPU_MASK 0xff0ffff0 @@ -74,6 +76,45 @@ ENTRY(exynos_cpu_resume_ns) mov r0, #SMC_CMD_C15RESUME dsb smc #0 +#ifdef CONFIG_CACHE_L2X0 + adr r0, 1f + ldr r2, [r0] + add r0, r2, r0 + + /* Check that the address has been initialised. */ + ldr r1, [r0, #L2X0_R_PHY_BASE] + teq r1, #0 + beq skip_l2x0 + + /* Check if controller has been enabled. */ + ldr r2, [r1, #L2X0_CTRL] + tst r2, #0x1 + bne skip_l2x0 + + ldr r1, [r0, #L2X0_R_TAG_LATENCY] + ldr r2, [r0, #L2X0_R_DATA_LATENCY] + ldr r3, [r0, #L2X0_R_PREFETCH_CTRL] + mov r0, #SMC_CMD_L2X0SETUP1 + smc #0 + + /* Reload saved regs pointer because smc corrupts registers. */ + adr r0, 1f + ldr r2, [r0] + add r0, r2, r0 + + ldr r1, [r0, #L2X0_R_PWR_CTRL] + ldr r2, [r0, #L2X0_R_AUX_CTRL] + mov r0, #SMC_CMD_L2X0SETUP2 + smc #0 + + mov r0, #SMC_CMD_L2X0INVALL + smc #0 + + mov r1, #1 + mov r0, #SMC_CMD_L2X0CTRL + smc #0 +skip_l2x0: +#endif /* CONFIG_CACHE_L2X0 */ skip_cp15: b cpu_resume ENDPROC(exynos_cpu_resume_ns) @@ -83,3 +124,8 @@ cp15_save_diag: .globl cp15_save_power cp15_save_power: .long 0 @ cp15 power control + +#ifdef CONFIG_CACHE_L2X0 + .align +1: .long l2x0_saved_regs - . +#endif /* CONFIG_CACHE_L2X0 */ From deb0559055f42ba396c838c9c47670eab1db666c Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Wed, 7 Jan 2015 12:30:24 +0100 Subject: [PATCH 582/788] ARM: dts: exynos4: Add nodes for L2 cache controller This patch adds device tree nodes for L2 cache controller present on Exynos4 SoCs. Signed-off-by: Tomasz Figa Signed-off-by: Marek Szyprowski Acked-by: Arnd Bergmann Acked-by: Kukjin Kim --- arch/arm/boot/dts/exynos4210.dtsi | 9 +++++++++ arch/arm/boot/dts/exynos4x12.dtsi | 14 ++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/arch/arm/boot/dts/exynos4210.dtsi b/arch/arm/boot/dts/exynos4210.dtsi index bcc9e63c807019..8e45ea44317e59 100644 --- a/arch/arm/boot/dts/exynos4210.dtsi +++ b/arch/arm/boot/dts/exynos4210.dtsi @@ -81,6 +81,15 @@ reg = <0x10023CA0 0x20>; }; + l2c: l2-cache-controller@10502000 { + compatible = "arm,pl310-cache"; + reg = <0x10502000 0x1000>; + cache-unified; + cache-level = <2>; + arm,tag-latency = <2 2 1>; + arm,data-latency = <2 2 1>; + }; + gic: interrupt-controller@10490000 { cpu-offset = <0x8000>; }; diff --git a/arch/arm/boot/dts/exynos4x12.dtsi b/arch/arm/boot/dts/exynos4x12.dtsi index 86e18004a6160e..85098ec688ec0f 100644 --- a/arch/arm/boot/dts/exynos4x12.dtsi +++ b/arch/arm/boot/dts/exynos4x12.dtsi @@ -54,6 +54,20 @@ reg = <0x10023CA0 0x20>; }; + l2c: l2-cache-controller@10502000 { + compatible = "arm,pl310-cache"; + reg = <0x10502000 0x1000>; + cache-unified; + cache-level = <2>; + arm,tag-latency = <2 2 1>; + arm,data-latency = <3 2 1>; + arm,double-linefill = <1>; + arm,double-linefill-incr = <0>; + arm,double-linefill-wrap = <1>; + arm,prefetch-drop = <1>; + arm,prefetch-offset = <7>; + }; + clock: clock-controller@10030000 { compatible = "samsung,exynos4412-clock"; reg = <0x10030000 0x20000>; From 2ae421a552742d54dde02eaaade4e4c6ef553c6b Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Tue, 13 Jan 2015 00:37:08 +0100 Subject: [PATCH 583/788] patchset: [PATCH v12 0/9] Enable L2 cache support on Exynos4210/4x12 SoCs --- PATCHES | 1 + 1 file changed, 1 insertion(+) create mode 100644 PATCHES diff --git a/PATCHES b/PATCHES new file mode 100644 index 00000000000000..688c5c8e63d9a6 --- /dev/null +++ b/PATCHES @@ -0,0 +1 @@ +[PATCH v12 0/9] Enable L2 cache support on Exynos4210/4x12 SoCs From be9cfbc377e5d3a4e3d32ffc6e458b0ee56b0936 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 2 Jan 2015 10:36:29 +0100 Subject: [PATCH 584/788] ARM: DTS: Exynos: convert to generic power domain bindings This patch replaces all custom samsung,power-domain device tree properties with generic power domain bindings and updates documentation Samsung's devices refering to old binding. Suggested-by: Kevin Hilman Signed-off-by: Marek Szyprowski Reviewed-by: Javier Martinez Canillas Tested-by: Javier Martinez Canillas --- .../bindings/arm/exynos/power_domain.txt | 2 +- .../bindings/iommu/samsung,sysmmu.txt | 6 ++--- .../devicetree/bindings/media/s5p-mfc.txt | 4 +-- .../devicetree/bindings/video/exynos_dsim.txt | 4 +-- .../bindings/video/samsung-fimd.txt | 4 +-- arch/arm/boot/dts/exynos3250.dtsi | 11 +++++--- arch/arm/boot/dts/exynos4.dtsi | 25 ++++++++++++------- arch/arm/boot/dts/exynos4210.dtsi | 1 + arch/arm/boot/dts/exynos4415.dtsi | 7 ++++++ arch/arm/boot/dts/exynos4x12.dtsi | 7 +++--- arch/arm/boot/dts/exynos5250.dtsi | 12 +++++---- arch/arm/boot/dts/exynos5420.dtsi | 10 +++++--- 12 files changed, 60 insertions(+), 33 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt index abde1ea8a1198a..f4445e5a2bbb7d 100644 --- a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt +++ b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt @@ -23,7 +23,7 @@ Optional Properties: devices in this power domain. Maximum of 4 pairs (N = 0 to 3) are supported currently. -Node of a device using power domains must have a samsung,power-domain property +Node of a device using power domains must have a power-domains property defined with a phandle to respective power domain. Example: diff --git a/Documentation/devicetree/bindings/iommu/samsung,sysmmu.txt b/Documentation/devicetree/bindings/iommu/samsung,sysmmu.txt index 6fa4c737af231e..729543c470468e 100644 --- a/Documentation/devicetree/bindings/iommu/samsung,sysmmu.txt +++ b/Documentation/devicetree/bindings/iommu/samsung,sysmmu.txt @@ -45,7 +45,7 @@ Required properties: Exynos4 SoCs, there needs no "master" clock. Exynos5 SoCs, some System MMUs must have "master" clocks. - clocks: Required if the System MMU is needed to gate its clock. -- samsung,power-domain: Required if the System MMU is needed to gate its power. +- power-domains: Required if the System MMU is needed to gate its power. Please refer to the following document: Documentation/devicetree/bindings/arm/exynos/power_domain.txt @@ -54,7 +54,7 @@ Examples: compatible = "samsung,exynos5-gsc"; reg = <0x13e00000 0x1000>; interrupts = <0 85 0>; - samsung,power-domain = <&pd_gsc>; + power-domains = <&pd_gsc>; clocks = <&clock CLK_GSCL0>; clock-names = "gscl"; }; @@ -66,5 +66,5 @@ Examples: interrupts = <2 0>; clock-names = "sysmmu", "master"; clocks = <&clock CLK_SMMU_GSCL0>, <&clock CLK_GSCL0>; - samsung,power-domain = <&pd_gsc>; + power-domains = <&pd_gsc>; }; diff --git a/Documentation/devicetree/bindings/media/s5p-mfc.txt b/Documentation/devicetree/bindings/media/s5p-mfc.txt index 3e3c5f3495708c..2d5787eac91af9 100644 --- a/Documentation/devicetree/bindings/media/s5p-mfc.txt +++ b/Documentation/devicetree/bindings/media/s5p-mfc.txt @@ -28,7 +28,7 @@ Required properties: for DMA contiguous memory allocation and its size. Optional properties: - - samsung,power-domain : power-domain property defined with a phandle + - power-domains : power-domain property defined with a phandle to respective power domain. Example: @@ -38,7 +38,7 @@ mfc: codec@13400000 { compatible = "samsung,mfc-v5"; reg = <0x13400000 0x10000>; interrupts = <0 94 0>; - samsung,power-domain = <&pd_mfc>; + power-domains = <&pd_mfc>; clocks = <&clock 273>; clock-names = "mfc"; }; diff --git a/Documentation/devicetree/bindings/video/exynos_dsim.txt b/Documentation/devicetree/bindings/video/exynos_dsim.txt index ca2b4aacd9afb5..802aa7ef64e5f7 100644 --- a/Documentation/devicetree/bindings/video/exynos_dsim.txt +++ b/Documentation/devicetree/bindings/video/exynos_dsim.txt @@ -21,7 +21,7 @@ Required properties: according to DSI host bindings (see MIPI DSI bindings [1]) Optional properties: - - samsung,power-domain: a phandle to DSIM power domain node + - power-domains: a phandle to DSIM power domain node Child nodes: Should contain DSI peripheral nodes (see MIPI DSI bindings [1]). @@ -53,7 +53,7 @@ Example: phy-names = "dsim"; vddcore-supply = <&vusb_reg>; vddio-supply = <&vmipi_reg>; - samsung,power-domain = <&pd_lcd0>; + power-domains = <&pd_lcd0>; #address-cells = <1>; #size-cells = <0>; samsung,pll-clock-frequency = <24000000>; diff --git a/Documentation/devicetree/bindings/video/samsung-fimd.txt b/Documentation/devicetree/bindings/video/samsung-fimd.txt index cf1af637102169..a8bbbde03e79e4 100644 --- a/Documentation/devicetree/bindings/video/samsung-fimd.txt +++ b/Documentation/devicetree/bindings/video/samsung-fimd.txt @@ -38,7 +38,7 @@ Required properties: property. Must contain "sclk_fimd" and "fimd". Optional Properties: -- samsung,power-domain: a phandle to FIMD power domain node. +- power-domains: a phandle to FIMD power domain node. - samsung,invert-vden: video enable signal is inverted - samsung,invert-vclk: video clock signal is inverted - display-timings: timing settings for FIMD, as described in document [1]. @@ -97,7 +97,7 @@ SoC specific DT entry: interrupts = <11 0>, <11 1>, <11 2>; clocks = <&clock 140>, <&clock 283>; clock-names = "sclk_fimd", "fimd"; - samsung,power-domain = <&pd_lcd0>; + power-domains = <&pd_lcd0>; status = "disabled"; }; diff --git a/arch/arm/boot/dts/exynos3250.dtsi b/arch/arm/boot/dts/exynos3250.dtsi index 22465494b79651..60307f6a82b7d9 100644 --- a/arch/arm/boot/dts/exynos3250.dtsi +++ b/arch/arm/boot/dts/exynos3250.dtsi @@ -141,26 +141,31 @@ pd_cam: cam-power-domain@10023C00 { compatible = "samsung,exynos4210-pd"; reg = <0x10023C00 0x20>; + #power-domain-cells = <0>; }; pd_mfc: mfc-power-domain@10023C40 { compatible = "samsung,exynos4210-pd"; reg = <0x10023C40 0x20>; + #power-domain-cells = <0>; }; pd_g3d: g3d-power-domain@10023C60 { compatible = "samsung,exynos4210-pd"; reg = <0x10023C60 0x20>; + #power-domain-cells = <0>; }; pd_lcd0: lcd0-power-domain@10023C80 { compatible = "samsung,exynos4210-pd"; reg = <0x10023C80 0x20>; + #power-domain-cells = <0>; }; pd_isp: isp-power-domain@10023CA0 { compatible = "samsung,exynos4210-pd"; reg = <0x10023CA0 0x20>; + #power-domain-cells = <0>; }; cmu: clock-controller@10030000 { @@ -235,7 +240,7 @@ interrupts = <0 84 0>, <0 85 0>, <0 86 0>; clocks = <&cmu CLK_SCLK_FIMD0>, <&cmu CLK_FIMD0>; clock-names = "sclk_fimd", "fimd"; - samsung,power-domain = <&pd_lcd0>; + power-domains = <&pd_lcd0>; samsung,sysreg = <&sys_reg>; status = "disabled"; }; @@ -245,7 +250,7 @@ reg = <0x11C80000 0x10000>; interrupts = <0 83 0>; samsung,phy-type = <0>; - samsung,power-domain = <&pd_lcd0>; + power-domains = <&pd_lcd0>; phys = <&mipi_phy 1>; phy-names = "dsim"; clocks = <&cmu CLK_DSIM0>, <&cmu CLK_SCLK_MIPI0>; @@ -327,7 +332,7 @@ interrupts = <0 102 0>; clock-names = "mfc", "sclk_mfc"; clocks = <&cmu CLK_MFC>, <&cmu CLK_SCLK_MFC>; - samsung,power-domain = <&pd_mfc>; + power-domains = <&pd_mfc>; status = "disabled"; }; diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi index 324b10c0af4891..2df87eff628f45 100644 --- a/arch/arm/boot/dts/exynos4.dtsi +++ b/arch/arm/boot/dts/exynos4.dtsi @@ -81,36 +81,43 @@ pd_mfc: mfc-power-domain@10023C40 { compatible = "samsung,exynos4210-pd"; reg = <0x10023C40 0x20>; + #power-domain-cells = <0>; }; pd_g3d: g3d-power-domain@10023C60 { compatible = "samsung,exynos4210-pd"; reg = <0x10023C60 0x20>; + #power-domain-cells = <0>; }; pd_lcd0: lcd0-power-domain@10023C80 { compatible = "samsung,exynos4210-pd"; reg = <0x10023C80 0x20>; + #power-domain-cells = <0>; }; pd_tv: tv-power-domain@10023C20 { compatible = "samsung,exynos4210-pd"; reg = <0x10023C20 0x20>; + #power-domain-cells = <0>; }; pd_cam: cam-power-domain@10023C00 { compatible = "samsung,exynos4210-pd"; reg = <0x10023C00 0x20>; + #power-domain-cells = <0>; }; pd_gps: gps-power-domain@10023CE0 { compatible = "samsung,exynos4210-pd"; reg = <0x10023CE0 0x20>; + #power-domain-cells = <0>; }; pd_gps_alive: gps-alive-power-domain@10023D00 { compatible = "samsung,exynos4210-pd"; reg = <0x10023D00 0x20>; + #power-domain-cells = <0>; }; gic: interrupt-controller@10490000 { @@ -147,7 +154,7 @@ compatible = "samsung,exynos4210-mipi-dsi"; reg = <0x11C80000 0x10000>; interrupts = <0 79 0>; - samsung,power-domain = <&pd_lcd0>; + power-domains = <&pd_lcd0>; phys = <&mipi_phy 1>; phy-names = "dsim"; clocks = <&clock CLK_DSIM0>, <&clock CLK_SCLK_MIPI0>; @@ -172,7 +179,7 @@ interrupts = <0 84 0>; clocks = <&clock CLK_FIMC0>, <&clock CLK_SCLK_FIMC0>; clock-names = "fimc", "sclk_fimc"; - samsung,power-domain = <&pd_cam>; + power-domains = <&pd_cam>; samsung,sysreg = <&sys_reg>; status = "disabled"; }; @@ -183,7 +190,7 @@ interrupts = <0 85 0>; clocks = <&clock CLK_FIMC1>, <&clock CLK_SCLK_FIMC1>; clock-names = "fimc", "sclk_fimc"; - samsung,power-domain = <&pd_cam>; + power-domains = <&pd_cam>; samsung,sysreg = <&sys_reg>; status = "disabled"; }; @@ -194,7 +201,7 @@ interrupts = <0 86 0>; clocks = <&clock CLK_FIMC2>, <&clock CLK_SCLK_FIMC2>; clock-names = "fimc", "sclk_fimc"; - samsung,power-domain = <&pd_cam>; + power-domains = <&pd_cam>; samsung,sysreg = <&sys_reg>; status = "disabled"; }; @@ -205,7 +212,7 @@ interrupts = <0 87 0>; clocks = <&clock CLK_FIMC3>, <&clock CLK_SCLK_FIMC3>; clock-names = "fimc", "sclk_fimc"; - samsung,power-domain = <&pd_cam>; + power-domains = <&pd_cam>; samsung,sysreg = <&sys_reg>; status = "disabled"; }; @@ -217,7 +224,7 @@ clocks = <&clock CLK_CSIS0>, <&clock CLK_SCLK_CSIS0>; clock-names = "csis", "sclk_csis"; bus-width = <4>; - samsung,power-domain = <&pd_cam>; + power-domains = <&pd_cam>; phys = <&mipi_phy 0>; phy-names = "csis"; status = "disabled"; @@ -232,7 +239,7 @@ clocks = <&clock CLK_CSIS1>, <&clock CLK_SCLK_CSIS1>; clock-names = "csis", "sclk_csis"; bus-width = <2>; - samsung,power-domain = <&pd_cam>; + power-domains = <&pd_cam>; phys = <&mipi_phy 2>; phy-names = "csis"; status = "disabled"; @@ -391,7 +398,7 @@ compatible = "samsung,mfc-v5"; reg = <0x13400000 0x10000>; interrupts = <0 94 0>; - samsung,power-domain = <&pd_mfc>; + power-domains = <&pd_mfc>; clocks = <&clock CLK_MFC>, <&clock CLK_SCLK_MFC>; clock-names = "mfc", "sclk_mfc"; status = "disabled"; @@ -641,7 +648,7 @@ interrupts = <11 0>, <11 1>, <11 2>; clocks = <&clock CLK_SCLK_FIMD0>, <&clock CLK_FIMD0>; clock-names = "sclk_fimd", "fimd"; - samsung,power-domain = <&pd_lcd0>; + power-domains = <&pd_lcd0>; samsung,sysreg = <&sys_reg>; status = "disabled"; }; diff --git a/arch/arm/boot/dts/exynos4210.dtsi b/arch/arm/boot/dts/exynos4210.dtsi index 8e45ea44317e59..6313097e127bf6 100644 --- a/arch/arm/boot/dts/exynos4210.dtsi +++ b/arch/arm/boot/dts/exynos4210.dtsi @@ -79,6 +79,7 @@ pd_lcd1: lcd1-power-domain@10023CA0 { compatible = "samsung,exynos4210-pd"; reg = <0x10023CA0 0x20>; + #power-domain-cells = <0>; }; l2c: l2-cache-controller@10502000 { diff --git a/arch/arm/boot/dts/exynos4415.dtsi b/arch/arm/boot/dts/exynos4415.dtsi index c1c9b37340d90e..2007def1ab43c9 100644 --- a/arch/arm/boot/dts/exynos4415.dtsi +++ b/arch/arm/boot/dts/exynos4415.dtsi @@ -131,36 +131,43 @@ pd_cam: cam-power-domain@10024000 { compatible = "samsung,exynos4210-pd"; reg = <0x10024000 0x20>; + #power-domain-cells = <0>; }; pd_tv: tv-power-domain@10024020 { compatible = "samsung,exynos4210-pd"; reg = <0x10024020 0x20>; + #power-domain-cells = <0>; }; pd_mfc: mfc-power-domain@10024040 { compatible = "samsung,exynos4210-pd"; reg = <0x10024040 0x20>; + #power-domain-cells = <0>; }; pd_g3d: g3d-power-domain@10024060 { compatible = "samsung,exynos4210-pd"; reg = <0x10024060 0x20>; + #power-domain-cells = <0>; }; pd_lcd0: lcd0-power-domain@10024080 { compatible = "samsung,exynos4210-pd"; reg = <0x10024080 0x20>; + #power-domain-cells = <0>; }; pd_isp0: isp0-power-domain@100240A0 { compatible = "samsung,exynos4210-pd"; reg = <0x100240A0 0x20>; + #power-domain-cells = <0>; }; pd_isp1: isp1-power-domain@100240E0 { compatible = "samsung,exynos4210-pd"; reg = <0x100240E0 0x20>; + #power-domain-cells = <0>; }; cmu: clock-controller@10030000 { diff --git a/arch/arm/boot/dts/exynos4x12.dtsi b/arch/arm/boot/dts/exynos4x12.dtsi index 85098ec688ec0f..f5e0ae780d6ce8 100644 --- a/arch/arm/boot/dts/exynos4x12.dtsi +++ b/arch/arm/boot/dts/exynos4x12.dtsi @@ -52,6 +52,7 @@ pd_isp: isp-power-domain@10023CA0 { compatible = "samsung,exynos4210-pd"; reg = <0x10023CA0 0x20>; + #power-domain-cells = <0>; }; l2c: l2-cache-controller@10502000 { @@ -209,7 +210,7 @@ compatible = "samsung,exynos4212-fimc-lite"; reg = <0x12390000 0x1000>; interrupts = <0 105 0>; - samsung,power-domain = <&pd_isp>; + power-domains = <&pd_isp>; clocks = <&clock CLK_FIMC_LITE0>; clock-names = "flite"; status = "disabled"; @@ -219,7 +220,7 @@ compatible = "samsung,exynos4212-fimc-lite"; reg = <0x123A0000 0x1000>; interrupts = <0 106 0>; - samsung,power-domain = <&pd_isp>; + power-domains = <&pd_isp>; clocks = <&clock CLK_FIMC_LITE1>; clock-names = "flite"; status = "disabled"; @@ -229,7 +230,7 @@ compatible = "samsung,exynos4212-fimc-is", "simple-bus"; reg = <0x12000000 0x260000>; interrupts = <0 90 0>, <0 95 0>; - samsung,power-domain = <&pd_isp>; + power-domains = <&pd_isp>; clocks = <&clock CLK_FIMC_LITE0>, <&clock CLK_FIMC_LITE1>, <&clock CLK_PPMUISPX>, <&clock CLK_PPMUISPMX>, diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index d75c89d7666a0a..9bb1b0b738f53d 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -93,11 +93,13 @@ pd_gsc: gsc-power-domain@10044000 { compatible = "samsung,exynos4210-pd"; reg = <0x10044000 0x20>; + #power-domain-cells = <0>; }; pd_mfc: mfc-power-domain@10044040 { compatible = "samsung,exynos4210-pd"; reg = <0x10044040 0x20>; + #power-domain-cells = <0>; }; clock: clock-controller@10010000 { @@ -222,7 +224,7 @@ compatible = "samsung,mfc-v6"; reg = <0x11000000 0x10000>; interrupts = <0 96 0>; - samsung,power-domain = <&pd_mfc>; + power-domains = <&pd_mfc>; clocks = <&clock CLK_MFC>; clock-names = "mfc"; }; @@ -682,7 +684,7 @@ compatible = "samsung,exynos5-gsc"; reg = <0x13e00000 0x1000>; interrupts = <0 85 0>; - samsung,power-domain = <&pd_gsc>; + power-domains = <&pd_gsc>; clocks = <&clock CLK_GSCL0>; clock-names = "gscl"; }; @@ -691,7 +693,7 @@ compatible = "samsung,exynos5-gsc"; reg = <0x13e10000 0x1000>; interrupts = <0 86 0>; - samsung,power-domain = <&pd_gsc>; + power-domains = <&pd_gsc>; clocks = <&clock CLK_GSCL1>; clock-names = "gscl"; }; @@ -700,7 +702,7 @@ compatible = "samsung,exynos5-gsc"; reg = <0x13e20000 0x1000>; interrupts = <0 87 0>; - samsung,power-domain = <&pd_gsc>; + power-domains = <&pd_gsc>; clocks = <&clock CLK_GSCL2>; clock-names = "gscl"; }; @@ -709,7 +711,7 @@ compatible = "samsung,exynos5-gsc"; reg = <0x13e30000 0x1000>; interrupts = <0 88 0>; - samsung,power-domain = <&pd_gsc>; + power-domains = <&pd_gsc>; clocks = <&clock CLK_GSCL3>; clock-names = "gscl"; }; diff --git a/arch/arm/boot/dts/exynos5420.dtsi b/arch/arm/boot/dts/exynos5420.dtsi index 6d38f8bfd0e68e..e5cb74d2b9f50e 100644 --- a/arch/arm/boot/dts/exynos5420.dtsi +++ b/arch/arm/boot/dts/exynos5420.dtsi @@ -178,7 +178,7 @@ interrupts = <0 96 0>; clocks = <&clock CLK_MFC>; clock-names = "mfc"; - samsung,power-domain = <&mfc_pd>; + power-domains = <&mfc_pd>; }; mmc_0: mmc@12200000 { @@ -250,11 +250,13 @@ gsc_pd: power-domain@10044000 { compatible = "samsung,exynos4210-pd"; reg = <0x10044000 0x20>; + #power-domain-cells = <0>; }; isp_pd: power-domain@10044020 { compatible = "samsung,exynos4210-pd"; reg = <0x10044020 0x20>; + #power-domain-cells = <0>; }; mfc_pd: power-domain@10044060 { @@ -263,11 +265,13 @@ clocks = <&clock CLK_FIN_PLL>, <&clock CLK_MOUT_SW_ACLK333>, <&clock CLK_MOUT_USER_ACLK333>; clock-names = "oscclk", "pclk0", "clk0"; + #power-domain-cells = <0>; }; msc_pd: power-domain@10044120 { compatible = "samsung,exynos4210-pd"; reg = <0x10044120 0x20>; + #power-domain-cells = <0>; }; pinctrl_0: pinctrl@13400000 { @@ -730,7 +734,7 @@ interrupts = <0 85 0>; clocks = <&clock CLK_GSCL0>; clock-names = "gscl"; - samsung,power-domain = <&gsc_pd>; + power-domains = <&gsc_pd>; }; gsc_1: video-scaler@13e10000 { @@ -739,7 +743,7 @@ interrupts = <0 86 0>; clocks = <&clock CLK_GSCL1>; clock-names = "gscl"; - samsung,power-domain = <&gsc_pd>; + power-domains = <&gsc_pd>; }; pmu_system_controller: system-controller@10040000 { From b60d114feafc27900936c5662549cdcd46571d1b Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 13 Jan 2015 10:39:46 +0100 Subject: [PATCH 585/788] ARM: Exynos: add support for sub-power domains This patch adds support for making one power domain a sub-domain of other domain. This is useful for modeling power dependences for devices like TV Mixer or Camera ISP, which needs to have more than one power domain enabled to be operational. Based on previous work by Amit Daniel Kachhap . Signed-off-by: Marek Szyprowski --- .../bindings/arm/exynos/power_domain.txt | 2 ++ arch/arm/mach-exynos/pm_domains.c | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt index f4445e5a2bbb7d..28918a9f81b226 100644 --- a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt +++ b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt @@ -22,6 +22,8 @@ Optional Properties: - pclkN, clkN: Pairs of parent of input clock and input clock to the devices in this power domain. Maximum of 4 pairs (N = 0 to 3) are supported currently. +- power-domains: generic power domain binding pointing to a master power domain + that the given domain is a part of Node of a device using power domains must have a power-domains property defined with a phandle to respective power domain. diff --git a/arch/arm/mach-exynos/pm_domains.c b/arch/arm/mach-exynos/pm_domains.c index 20f267121b3e78..37266a8264372a 100644 --- a/arch/arm/mach-exynos/pm_domains.c +++ b/arch/arm/mach-exynos/pm_domains.c @@ -161,6 +161,34 @@ static __init int exynos4_pm_init_power_domain(void) of_genpd_add_provider_simple(np, &pd->pd); } + /* Assign the child power domains to their parents */ + for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") { + struct generic_pm_domain *child_domain, *parent_domain; + struct of_phandle_args args; + + args.np = np; + args.args_count = 0; + child_domain = of_genpd_get_from_provider(&args); + if (!child_domain) + continue; + + if (of_parse_phandle_with_args(np, "power-domains", + "#power-domain-cells", 0, &args) != 0) + continue; + + parent_domain = of_genpd_get_from_provider(&args); + if (!parent_domain) + continue; + + if (pm_genpd_add_subdomain(parent_domain, child_domain)) + pr_warn("%s failed to add subdomain: %s\n", + parent_domain->name, child_domain->name); + else + pr_info("%s has as child subdomain: %s.\n", + parent_domain->name, child_domain->name); + of_node_put(np); + } + return 0; } arch_initcall(exynos4_pm_init_power_domain); From 03c83ad5c9e886341bb3975a961ef9d06509ad43 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 13 Jan 2015 10:39:47 +0100 Subject: [PATCH 586/788] ARM: dts: exynos4: add hdmi related nodes This patch adds entries for HDMI, Mixer and i2c with hdmi-phy modules found in Exynos 4210 and 4x12 SoCs. Signed-off-by: Marek Szyprowski --- arch/arm/boot/dts/exynos4.dtsi | 40 +++++++++++++++++++++++++++++++ arch/arm/boot/dts/exynos4210.dtsi | 8 +++++++ arch/arm/boot/dts/exynos4x12.dtsi | 11 +++++++++ 3 files changed, 59 insertions(+) diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi index 2df87eff628f45..4491c22259b7f7 100644 --- a/arch/arm/boot/dts/exynos4.dtsi +++ b/arch/arm/boot/dts/exynos4.dtsi @@ -38,6 +38,7 @@ i2c5 = &i2c_5; i2c6 = &i2c_6; i2c7 = &i2c_7; + i2c8 = &i2c_8; csis0 = &csis_0; csis1 = &csis_1; fimc0 = &fimc_0; @@ -544,6 +545,22 @@ status = "disabled"; }; + i2c_8: i2c@138E0000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "samsung,s3c2440-hdmiphy-i2c"; + reg = <0x138E0000 0x100>; + interrupts = <0 93 0>; + clocks = <&clock CLK_I2C_HDMI>; + clock-names = "i2c"; + status = "disabled"; + + hdmi_i2c_phy: hdmiphy@38 { + compatible = "exynos4210-hdmiphy"; + reg = <0x38>; + }; + }; + spi_0: spi@13920000 { compatible = "samsung,exynos4210-spi"; reg = <0x13920000 0x100>; @@ -660,4 +677,27 @@ clocks = <&clock CLK_SSS>; clock-names = "secss"; }; + + hdmi: hdmi@12D00000 { + compatible = "samsung,exynos4210-hdmi"; + reg = <0x12D00000 0x70000>; + interrupts = <0 92 0>; + clock-names = "hdmi", "sclk_hdmi", "sclk_pixel", "sclk_hdmiphy", + "mout_hdmi"; + clocks = <&clock CLK_HDMI>, <&clock CLK_SCLK_HDMI>, + <&clock CLK_SCLK_PIXEL>, <&clock CLK_SCLK_HDMIPHY>, + <&clock CLK_MOUT_HDMI>; + phy = <&hdmi_i2c_phy>; + power-domains = <&pd_tv>; + samsung,syscon-phandle = <&pmu_system_controller>; + status = "disabled"; + }; + + mixer: mixer@12C10000 { + compatible = "samsung,exynos4210-mixer"; + interrupts = <0 91 0>; + reg = <0x12C10000 0x2100>, <0x12c00000 0x300>; + power-domains = <&pd_tv>; + status = "disabled"; + }; }; diff --git a/arch/arm/boot/dts/exynos4210.dtsi b/arch/arm/boot/dts/exynos4210.dtsi index 6313097e127bf6..a672ab9fe89967 100644 --- a/arch/arm/boot/dts/exynos4210.dtsi +++ b/arch/arm/boot/dts/exynos4210.dtsi @@ -202,4 +202,12 @@ samsung,lcd-wb; }; }; + + mixer: mixer@12C10000 { + clock-names = "mixer", "sclk_hdmi", "vp", "mout_mixer", + "sclk_mixer"; + clocks = <&clock CLK_MIXER>, <&clock CLK_SCLK_HDMI>, + <&clock CLK_VP>, <&clock CLK_MOUT_MIXER>, + <&clock CLK_SCLK_MIXER>; + }; }; diff --git a/arch/arm/boot/dts/exynos4x12.dtsi b/arch/arm/boot/dts/exynos4x12.dtsi index f5e0ae780d6ce8..c8ee54da299790 100644 --- a/arch/arm/boot/dts/exynos4x12.dtsi +++ b/arch/arm/boot/dts/exynos4x12.dtsi @@ -297,4 +297,15 @@ clock-names = "tmu_apbif"; status = "disabled"; }; + + hdmi: hdmi@12D00000 { + compatible = "samsung,exynos4212-hdmi"; + }; + + mixer: mixer@12C10000 { + compatible = "samsung,exynos4212-mixer"; + clock-names = "mixer", "sclk_hdmi", "vp"; + clocks = <&clock CLK_MIXER>, <&clock CLK_SCLK_HDMI>, + <&clock CLK_VP>; + }; }; From 92e8ad5e4679680ab332da214c7bdc0761b76b7a Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 13 Jan 2015 10:39:48 +0100 Subject: [PATCH 587/788] ARM: dts: exynos4: add dependency between TV and LCD0 power domains TV Mixer needs both TV and LCD0 domains enabled to be fully operational. This dependency is modelled by making TV power domains a sub-domain of LCD0 power domain. Signed-off-by: Marek Szyprowski --- arch/arm/boot/dts/exynos4.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi index 4491c22259b7f7..c4d80bb91c321a 100644 --- a/arch/arm/boot/dts/exynos4.dtsi +++ b/arch/arm/boot/dts/exynos4.dtsi @@ -101,6 +101,7 @@ compatible = "samsung,exynos4210-pd"; reg = <0x10023C20 0x20>; #power-domain-cells = <0>; + power-domains = <&pd_lcd0>; }; pd_cam: cam-power-domain@10023C00 { From 77605028063f00b6c046b00a38fa35f4503d5c3a Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 13 Jan 2015 10:39:49 +0100 Subject: [PATCH 588/788] ARM: dts: exynos4412-odroid: enable hdmi support This patch adds nodes specific to Exynos4412 based Odroid X/X2/U2/U3 boards required for enabling HDMI display. Signed-off-by: Marek Szyprowski --- .../boot/dts/exynos4412-odroid-common.dtsi | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index 3fbf588682b94f..e10efa88807a9a 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -231,6 +231,20 @@ regulator-always-on; }; + ldo8_reg: ldo@8 { + regulator-compatible = "LDO8"; + regulator-name = "VDD10_HDMI_1.0V"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + }; + + ldo10_reg: ldo@10 { + regulator-compatible = "LDO10"; + regulator-name = "VDDQ_MIPIHSI_1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + ldo11_reg: LDO11 { regulator-name = "VDD18_ABB1_1.8V"; regulator-min-microvolt = <1800000>; @@ -389,6 +403,31 @@ ehci: ehci@12580000 { status = "okay"; }; + + mixer: mixer@12C10000 { + status = "okay"; + }; + + hdmi@12D00000 { + hpd-gpio = <&gpx3 7 0>; + pinctrl-names = "default"; + pinctrl-0 = <&hdmi_hpd>; + vdd-supply = <&ldo8_reg>; + vdd_osc-supply = <&ldo10_reg>; + vdd_pll-supply = <&ldo8_reg>; + ddc = <&hdmi_ddc>; + status = "okay"; + }; + + hdmi_ddc: i2c@13880000 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c2_bus>; + }; + + i2c@138E0000 { + status = "okay"; + }; }; &pinctrl_1 { @@ -403,4 +442,9 @@ samsung,pin-pud = <0>; samsung,pin-drv = <0>; }; + + hdmi_hpd: hdmi-hpd { + samsung,pins = "gpx3-7"; + samsung,pin-pud = <1>; + }; }; From e2ce0963786b165ebb561a2977a960cac52b8f4e Mon Sep 17 00:00:00 2001 From: Tomasz Stanislawski Date: Tue, 13 Jan 2015 10:41:29 +0100 Subject: [PATCH 589/788] ARM: dts: exynos4210-universal_c210: enable hdmi support This patch adds configuration of hw modules required to enable HDMI support on Universal C210 board. Signed-off-by: Tomasz Stanislawski Signed-off-by: Marek Szyprowski --- .../boot/dts/exynos4210-universal_c210.dts | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/arch/arm/boot/dts/exynos4210-universal_c210.dts b/arch/arm/boot/dts/exynos4210-universal_c210.dts index aaf0cae4f5e87b..01f7d3cfdd021d 100644 --- a/arch/arm/boot/dts/exynos4210-universal_c210.dts +++ b/arch/arm/boot/dts/exynos4210-universal_c210.dts @@ -503,6 +503,63 @@ assigned-clock-rates = <0>, <160000000>; }; }; + + hdmi_en: voltage-regulator-hdmi-5v { + compatible = "regulator-fixed"; + regulator-name = "HDMI_5V"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpe0 1 0>; + enable-active-high; + }; + + hdmi_ddc: i2c-ddc { + compatible = "i2c-gpio"; + gpios = <&gpe4 2 0 &gpe4 3 0>; + i2c-gpio,delay-us = <100>; + #address-cells = <1>; + #size-cells = <0>; + + pinctrl-0 = <&i2c_ddc_bus>; + pinctrl-names = "default"; + status = "okay"; + }; + + mixer@12C10000 { + status = "okay"; + }; + + hdmi@12D00000 { + hpd-gpio = <&gpx3 7 0>; + pinctrl-names = "default"; + pinctrl-0 = <&hdmi_hpd>; + hdmi-en-supply = <&hdmi_en>; + vdd-supply = <&ldo3_reg>; + vdd_osc-supply = <&ldo4_reg>; + vdd_pll-supply = <&ldo3_reg>; + ddc = <&hdmi_ddc>; + status = "okay"; + }; + + i2c@138E0000 { + status = "okay"; + }; +}; + +&pinctrl_1 { + hdmi_hpd: hdmi-hpd { + samsung,pins = "gpx3-7"; + samsung,pin-pud = <0>; + }; +}; + +&pinctrl_0 { + i2c_ddc_bus: i2c-ddc-bus { + samsung,pins = "gpe4-2", "gpe4-3"; + samsung,pin-function = <2>; + samsung,pin-pud = <3>; + samsung,pin-drv = <0>; + }; }; &mdma1 { From 8d31da1518222d80e3debd4e54d50928aa51cf71 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Tue, 13 Jan 2015 10:41:45 +0100 Subject: [PATCH 590/788] ARM: dts: exynos5250: add display power domain The patch adds domain definition and references to it in appropriate devices. Signed-off-by: Andrzej Hajda [mszyprow: rebased onto generic power domains dt bindings] Signed-off-by: Marek Szyprowski --- arch/arm/boot/dts/exynos5250.dtsi | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index 9bb1b0b738f53d..5d0bca15957bba 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -102,6 +102,12 @@ #power-domain-cells = <0>; }; + pd_disp1: disp1-power-domain@100440A0 { + compatible = "samsung,exynos4210-pd"; + reg = <0x100440A0 0x20>; + #power-domain-cells = <0>; + }; + clock: clock-controller@10010000 { compatible = "samsung,exynos5250-clock"; reg = <0x10010000 0x30000>; @@ -719,6 +725,7 @@ hdmi: hdmi { compatible = "samsung,exynos4212-hdmi"; reg = <0x14530000 0x70000>; + power-domains = <&pd_disp1>; interrupts = <0 95 0>; clocks = <&clock CLK_HDMI>, <&clock CLK_SCLK_HDMI>, <&clock CLK_SCLK_PIXEL>, <&clock CLK_SCLK_HDMIPHY>, @@ -731,6 +738,7 @@ mixer { compatible = "samsung,exynos5250-mixer"; reg = <0x14450000 0x10000>; + power-domains = <&pd_disp1>; interrupts = <0 94 0>; clocks = <&clock CLK_MIXER>, <&clock CLK_SCLK_HDMI>; clock-names = "mixer", "sclk_hdmi"; @@ -743,6 +751,7 @@ }; dp: dp-controller@145B0000 { + power-domains = <&pd_disp1>; clocks = <&clock CLK_DP>; clock-names = "dp"; phys = <&dp_phy>; @@ -750,6 +759,7 @@ }; fimd: fimd@14400000 { + power-domains = <&pd_disp1>; clocks = <&clock CLK_SCLK_FIMD1>, <&clock CLK_FIMD1>; clock-names = "sclk_fimd", "fimd"; }; From 8e97ed6e7dbfed3aec31ebd6845dbbe0af518d87 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Tue, 13 Jan 2015 18:31:45 +0100 Subject: [PATCH 591/788] patchset: [PATCH v2 0/6] Enable HDMI support on Exynos platforms --- PATCHES | 1 + 1 file changed, 1 insertion(+) diff --git a/PATCHES b/PATCHES index 688c5c8e63d9a6..fbfc637d588287 100644 --- a/PATCHES +++ b/PATCHES @@ -1 +1,2 @@ [PATCH v12 0/9] Enable L2 cache support on Exynos4210/4x12 SoCs +[PATCH v2 0/6] Enable HDMI support on Exynos platforms From dc95e3e0aff1ac4ebd22aafed2aed37cddb3574a Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Mon, 19 Jan 2015 17:22:35 +0100 Subject: [PATCH 592/788] drm/exynos: fix mixer start sequence Mixed need to have hdmi clock enabled to properly perform power on/off sequences, so add handling of this clock directly to the mixer driver. Suggested-by: Andrzej Hajda Signed-off-by: Marek Szyprowski --- drivers/gpu/drm/exynos/exynos_mixer.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index bb403e8c4cbb6f..6936bcdeffd381 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -72,6 +72,7 @@ struct mixer_resources { spinlock_t reg_slock; struct clk *mixer; struct clk *vp; + struct clk *hdmi; struct clk *sclk_mixer; struct clk *sclk_hdmi; struct clk *mout_mixer; @@ -774,6 +775,12 @@ static int mixer_resources_init(struct mixer_context *mixer_ctx) return -ENODEV; } + mixer_res->hdmi = devm_clk_get(dev, "hdmi"); + if (IS_ERR(mixer_res->hdmi)) { + dev_err(dev, "failed to get clock 'hdmi'\n"); + return -ENODEV; + } + mixer_res->sclk_hdmi = devm_clk_get(dev, "sclk_hdmi"); if (IS_ERR(mixer_res->sclk_hdmi)) { dev_err(dev, "failed to get clock 'sclk_hdmi'\n"); @@ -1100,6 +1107,7 @@ static void mixer_poweron(struct exynos_drm_manager *mgr) pm_runtime_get_sync(ctx->dev); clk_prepare_enable(res->mixer); + clk_prepare_enable(res->hdmi); if (ctx->vp_enabled) { clk_prepare_enable(res->vp); if (ctx->has_sclk) @@ -1139,6 +1147,7 @@ static void mixer_poweroff(struct exynos_drm_manager *mgr) ctx->powered = false; mutex_unlock(&ctx->mixer_mutex); + clk_disable_unprepare(res->hdmi); clk_disable_unprepare(res->mixer); if (ctx->vp_enabled) { clk_disable_unprepare(res->vp); From 455e60726d912f721c69f58dbb54c8ccfc7ca342 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Mon, 19 Jan 2015 17:16:51 +0100 Subject: [PATCH 593/788] ARM: DTS: Exynos: add 'hdmi' clock to mixer nodes Signed-off-by: Marek Szyprowski --- arch/arm/boot/dts/exynos4210.dtsi | 8 ++++---- arch/arm/boot/dts/exynos4x12.dtsi | 6 +++--- arch/arm/boot/dts/exynos5250.dtsi | 5 +++-- arch/arm/boot/dts/exynos5420.dtsi | 5 +++-- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/arch/arm/boot/dts/exynos4210.dtsi b/arch/arm/boot/dts/exynos4210.dtsi index a672ab9fe89967..f551fb55677ac0 100644 --- a/arch/arm/boot/dts/exynos4210.dtsi +++ b/arch/arm/boot/dts/exynos4210.dtsi @@ -204,10 +204,10 @@ }; mixer: mixer@12C10000 { - clock-names = "mixer", "sclk_hdmi", "vp", "mout_mixer", + clock-names = "mixer", "hdmi", "sclk_hdmi", "vp", "mout_mixer", "sclk_mixer"; - clocks = <&clock CLK_MIXER>, <&clock CLK_SCLK_HDMI>, - <&clock CLK_VP>, <&clock CLK_MOUT_MIXER>, - <&clock CLK_SCLK_MIXER>; + clocks = <&clock CLK_MIXER>, <&clock CLK_HDMI>, + <&clock CLK_SCLK_HDMI>, <&clock CLK_VP>, + <&clock CLK_MOUT_MIXER>, <&clock CLK_SCLK_MIXER>; }; }; diff --git a/arch/arm/boot/dts/exynos4x12.dtsi b/arch/arm/boot/dts/exynos4x12.dtsi index c8ee54da299790..d97e018e61e143 100644 --- a/arch/arm/boot/dts/exynos4x12.dtsi +++ b/arch/arm/boot/dts/exynos4x12.dtsi @@ -304,8 +304,8 @@ mixer: mixer@12C10000 { compatible = "samsung,exynos4212-mixer"; - clock-names = "mixer", "sclk_hdmi", "vp"; - clocks = <&clock CLK_MIXER>, <&clock CLK_SCLK_HDMI>, - <&clock CLK_VP>; + clock-names = "mixer", "hdmi", "sclk_hdmi", "vp"; + clocks = <&clock CLK_MIXER>, <&clock CLK_HDMI>, + <&clock CLK_SCLK_HDMI>, <&clock CLK_VP>; }; }; diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index 5d0bca15957bba..ae22bd98f956c4 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -740,8 +740,9 @@ reg = <0x14450000 0x10000>; power-domains = <&pd_disp1>; interrupts = <0 94 0>; - clocks = <&clock CLK_MIXER>, <&clock CLK_SCLK_HDMI>; - clock-names = "mixer", "sclk_hdmi"; + clocks = <&clock CLK_MIXER>, <&clock CLK_HDMI>, + <&clock CLK_SCLK_HDMI>; + clock-names = "mixer", "hdmi", "sclk_hdmi"; }; dp_phy: video-phy@10040720 { diff --git a/arch/arm/boot/dts/exynos5420.dtsi b/arch/arm/boot/dts/exynos5420.dtsi index e5cb74d2b9f50e..8e464314260c44 100644 --- a/arch/arm/boot/dts/exynos5420.dtsi +++ b/arch/arm/boot/dts/exynos5420.dtsi @@ -724,8 +724,9 @@ compatible = "samsung,exynos5420-mixer"; reg = <0x14450000 0x10000>; interrupts = <0 94 0>; - clocks = <&clock CLK_MIXER>, <&clock CLK_SCLK_HDMI>; - clock-names = "mixer", "sclk_hdmi"; + clocks = <&clock CLK_MIXER>, <&clock CLK_HDMI>, + <&clock CLK_SCLK_HDMI>; + clock-names = "mixer", "hdmi", "sclk_hdmi"; }; gsc_0: video-scaler@13e00000 { From e8f65c6ed031256b5ad4602ddc6958a14c8fd084 Mon Sep 17 00:00:00 2001 From: Alban Browaeys Date: Sun, 9 Nov 2014 02:09:45 +0100 Subject: [PATCH 594/788] drm: exynos/g2d: Fix cmdlist free collision The cmdlist was freed twice: Internal error: Oops: 5 [#1] PREEMPT SMP ARM Modules linked in: cfg80211 bnep nfsd auth_rpcgss[ 372.570601] CPU: 0 PID: 71 Comm: kworker/u8:2 Tainted: G C O 3.18.0-rc3-00168-gd9d00f7-dirty #70 Workqueue: g2d g2d_runqueue_worker [exynosdrm] task: ed300000 ti: eb6a4000 task.ti: eb6a4000 PC is at g2d_unmap_cmdlist_gem+0x3c/0xe8 [exynosdrm] LR is at vprintk_emit+0x4ec/0x570 pc : [] lr : [] psr: 000f0053 sp : eb6a5e38 ip : eb6a5da0 fp : eb6a5e74 r10: 00000000 r9 : 00000000 r8 : eafdcca4 r7 : 6b6b6b6b r6 : e3ae3000 r5 : e3ae3008 r4 : 6b6b6b6b r3 : 6b6b6b7b r2 : 00000000 r1 : c0864ac4 r0 : 00000017 Flags: nzcv IRQs on FIQs off Mode SVC_32 ISA ARM Segment kernel Control: 10c5387d Table: 67a6c04a DAC: 00000015 Process kworker/u8:2 (pid: 71, stack limit = 0xeb6a4248) Stack: (0xeb6a5e38 to 0xeb6a6000) 5e20: e3ae3000 6b6b6b6b 5e40: 6b6b6b7b eafdcc50 00000000 6b6b6b6b e3ae3008 e3ae3000 eafdcc50 eafdcca4 5e60: 00000000 00000000 eb6a5e9c eb6a5e78 bf059a00 bf0598d8 a56b6b6b eafdcc64 5e80: eafdcce4 eafdcc50 eb6a4000 eddad400 eb6a5ebc eb6a5ea0 bf05a5b4 bf0599c0 5ea0: bf05a55c eafdcc64 eb67f400 edc15580 eb6a5efc eb6a5ec0 c003fa58 bf05a568 5ec0: edc15580 eb6a4000 eb6a4000 00000000 eb67f400 edc15580 edc155a0 eb6a4000 5ee0: eb6a4000 eb67f418 eb67f400 00000088 eb6a5f44 eb6a5f00 c00405d4 c003f828 5f00: eb6a5f24 eb6a5f10 c06cc3c4 c0a0f100 00000000 c0a728f7 c00402bc eb68af00 5f20: 00000000 eb67f400 c00402bc 00000000 00000000 00000000 eb6a5fac eb6a5f48 5f40: c0045544 c00402c8 c0a2e138 00000000 eb6a5f6c eb67f400 00000000 00000000 5f60: dead4ead ffffffff ffffffff eb6a5f6c eb6a5f6c 00000000 00000000 dead4ead 5f80: ffffffff ffffffff eb6a5f88 eb6a5f88 eb68af00 c0045450 00000000 00000000 5fa0: 00000000 eb6a5fb0 c000f918 c004545c 00000000 00000000 00000000 00000000 5fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 5fe0: 00000000 00000000 00000000 00000000 00000013 00000000 5a5a5a5a 5a5a5a5a [] (g2d_unmap_cmdlist_gem [exynosdrm]) from [] (g2d_free_runqueue_node+0x4c/0xa8 [exynosdrm]) [] (g2d_free_runqueue_node [exynosdrm]) from [] (g2d_runqueue_worker+0x58/0x80 [exynosdrm]) [] (g2d_runqueue_worker [exynosdrm]) from [] (process_one_work+0x23c/0x44c) [] (process_one_work) from [] (worker_thread+0x318/0x51c) [] (worker_thread) from [] (kthread+0xf4/0xfc) [] (kthread) from [] (ret_from_fork+0x14/0x20) Code: eb59a21a e2973010 e50b3034 0a00000c (e5973010) --- drivers/gpu/drm/exynos/exynos_drm_g2d.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index 81a25083080845..eb1420bcd189ef 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -1296,7 +1296,9 @@ int exynos_g2d_exec_ioctl(struct drm_device *drm_dev, void *data, goto out; wait_for_completion(&runqueue_node->complete); + mutex_lock(&g2d->runqueue_mutex); g2d_free_runqueue_node(g2d, runqueue_node); + mutex_unlock(&g2d->runqueue_mutex); out: return 0; From f0366dfbe2a3ee4ac87e09b6e4a6f4065d42aaf1 Mon Sep 17 00:00:00 2001 From: Alban Browaeys Date: Mon, 10 Nov 2014 19:38:23 +0100 Subject: [PATCH 595/788] drm: exynos/g2d: do not fetch the dma_address directly Go through sg_dma_address to get the scatterlist DMA address. --- drivers/gpu/drm/exynos/exynos_drm_g2d.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index eb1420bcd189ef..89d901e7433174 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -529,7 +529,7 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev, goto err_sg_free_table; } - g2d_userptr->dma_addr = sgt->sgl[0].dma_address; + g2d_userptr->dma_addr = sg_dma_address(sgt->sgl); g2d_userptr->userptr = userptr; list_add_tail(&g2d_userptr->list, &g2d_priv->userptr_list); From 65fb249599305058b16670d0aee6104a5ba6ee23 Mon Sep 17 00:00:00 2001 From: Alban Browaeys Date: Mon, 10 Nov 2014 19:41:44 +0100 Subject: [PATCH 596/788] drm: exynos/gem: harden exynos_gem_map_sgt_with_dma Return EIO if we receive an error or zero from dma_map_sg. Previously zero 'nents' would result in returning successfully. --- drivers/gpu/drm/exynos/exynos_drm_gem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index 0d5b9698d38402..2fcd4f54760487 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c @@ -484,10 +484,10 @@ int exynos_gem_map_sgt_with_dma(struct drm_device *drm_dev, mutex_lock(&drm_dev->struct_mutex); nents = dma_map_sg(drm_dev->dev, sgt->sgl, sgt->nents, dir); - if (!nents) { + if (nents <= 0) { DRM_ERROR("failed to map sgl with dma.\n"); mutex_unlock(&drm_dev->struct_mutex); - return nents; + return -EIO; } mutex_unlock(&drm_dev->struct_mutex); From 4561f18c5b69c19fb6c0a30d7a5cbcf1766352dd Mon Sep 17 00:00:00 2001 From: Alban Browaeys Date: Mon, 10 Nov 2014 19:45:05 +0100 Subject: [PATCH 597/788] drm: exynos/g2d: check for aligned userptr start and size --- drivers/gpu/drm/exynos/exynos_drm_g2d.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index 89d901e7433174..d598edbcb2cfe9 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -414,6 +414,13 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev, unsigned long start, end; unsigned int npages, offset; int ret; + const unsigned long dma_align = dma_get_cache_alignment(); + + /* check that both userptr and size are aligned. */ + if (!IS_ALIGNED(userptr | size, dma_align)) { + DRM_ERROR("user data must be aligned to %lu bytes\n", dma_align); + return ERR_PTR(-EINVAL); + } if (!size) { DRM_ERROR("invalid userptr size.\n"); From 346fd6aa9cb32d6bb8a552cca6e57bf05c0d9961 Mon Sep 17 00:00:00 2001 From: Alban Browaeys Date: Mon, 10 Nov 2014 19:45:43 +0100 Subject: [PATCH 598/788] drm: exynos/g2d: check for contiguity of DMA addresses Do a check to avoid returning a DMA address to a non-contiguous set of buffers if not under IOMMU mapping facilities. --- drivers/gpu/drm/exynos/exynos_drm_g2d.c | 35 ++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index d598edbcb2cfe9..29bc1b558e7e31 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -398,6 +398,24 @@ static void g2d_userptr_put_dma_addr(struct drm_device *drm_dev, kfree(g2d_userptr); } + +static unsigned long g2d_get_contiguous_size(struct sg_table *sgt) +{ + struct scatterlist *s; + dma_addr_t expected = sg_dma_address(sgt->sgl); + unsigned int i; + unsigned long size = 0; + + for_each_sg(sgt->sgl, s, sgt->nents, i) { + if (sg_dma_address(s) != expected) + break; + expected = sg_dma_address(s) + sg_dma_len(s); + size += sg_dma_len(s); + } + return size; +} + + static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev, unsigned long userptr, unsigned long size, @@ -415,6 +433,7 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev, unsigned int npages, offset; int ret; const unsigned long dma_align = dma_get_cache_alignment(); + unsigned long contig_size; /* check that both userptr and size are aligned. */ if (!IS_ALIGNED(userptr | size, dma_align)) { @@ -501,7 +520,6 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev, goto err_free_pages; } - g2d_userptr->size = size; ret = exynos_gem_get_pages_from_userptr(start & PAGE_MASK, npages, pages, vma); @@ -536,8 +554,19 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev, goto err_sg_free_table; } + if (!is_drm_iommu_supported(drm_dev)) { + contig_size = g2d_get_contiguous_size(sgt); + if (contig_size < size) { + pr_err("contiguous mapping is too small %lu/%lu\n", + contig_size, size); + ret = -EFAULT; + goto err_sg_unmap; + } + } + g2d_userptr->dma_addr = sg_dma_address(sgt->sgl); g2d_userptr->userptr = userptr; + g2d_userptr->size = size; list_add_tail(&g2d_userptr->list, &g2d_priv->userptr_list); @@ -550,6 +579,10 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev, return &g2d_userptr->dma_addr; +err_sg_unmap: + exynos_gem_unmap_sgt_from_dma(drm_dev, g2d_userptr->sgt, + DMA_BIDIRECTIONAL); + err_sg_free_table: sg_free_table(sgt); From 5a366deb6120f8378f3ec1138911f155dd8f95f8 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 19 Dec 2014 14:55:21 +0100 Subject: [PATCH 599/788] ASoC: samsung: i2s: Remove unused gpios field from struct i2s The 'gpios' field in 'struct i2s' is now unused, this change seems to be missing in commit 0429ffeff460c4302bd1520e6 ("ASoC: samsung: Remove obsolete GPIO based DT pinmuxing"). Signed-off-by: Sylwester Nawrocki Signed-off-by: Mark Brown --- sound/soc/samsung/i2s.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index b5a80c528d869e..86491c9f121fe6 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -95,7 +95,6 @@ struct i2s_dai { u32 suspend_i2smod; u32 suspend_i2scon; u32 suspend_i2spsr; - unsigned long gpios[7]; /* i2s gpio line numbers */ const struct samsung_i2s_variant_regs *variant_regs; }; From 8fc5145f2da4bca8ada7ceb9846e764b9f13b15d Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 14 Jan 2015 19:42:28 +0100 Subject: [PATCH 600/788] ASoC: samsung: i2s: samsung_i2s_get_driver_data() cleanup Tidy up the samsung_i2s_get_driver_data() function by using IS_ENABLE() instead of #ifdef and add missing braces for the 'else' part. Also ensure we are not dereferencing NULL 'match' pointer. Signed-off-by: Sylwester Nawrocki --- sound/soc/samsung/i2s.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 86491c9f121fe6..e5473ee799d770 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1123,15 +1123,14 @@ static const struct of_device_id exynos_i2s_match[]; static inline const struct samsung_i2s_dai_data *samsung_i2s_get_driver_data( struct platform_device *pdev) { -#ifdef CONFIG_OF - if (pdev->dev.of_node) { + if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { const struct of_device_id *match; match = of_match_node(exynos_i2s_match, pdev->dev.of_node); - return match->data; - } else -#endif + return match ? match->data : NULL; + } else { return (struct samsung_i2s_dai_data *) platform_get_device_id(pdev)->driver_data; + } } #ifdef CONFIG_PM From eea25f54c0c43e5b213f8f7f67e1cbee94ef6e3d Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 14 Jan 2015 19:42:29 +0100 Subject: [PATCH 601/788] ASoC: samsung: i2s: Add return value checks in probe() These functions may fail so let's properly report any errors. Signed-off-by: Sylwester Nawrocki --- sound/soc/samsung/i2s.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index e5473ee799d770..aa52b41f174946 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1173,11 +1173,13 @@ static int samsung_i2s_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Unable to get drvdata\n"); return -EFAULT; } - devm_snd_soc_register_component(&sec_dai->pdev->dev, + ret = devm_snd_soc_register_component(&sec_dai->pdev->dev, &samsung_i2s_component, &sec_dai->i2s_dai_drv, 1); - samsung_asoc_dma_platform_register(&pdev->dev); - return 0; + if (ret != 0) + return ret; + + return samsung_asoc_dma_platform_register(&pdev->dev); } pri_dai = i2s_alloc_dai(pdev, false); @@ -1290,7 +1292,9 @@ static int samsung_i2s_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); - samsung_asoc_dma_platform_register(&pdev->dev); + ret = samsung_asoc_dma_platform_register(&pdev->dev); + if (ret != 0) + return ret; return 0; err: From 02aa9e0020a35578895d3bc9ea68a4fdb89e182f Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 14 Jan 2015 19:42:30 +0100 Subject: [PATCH 602/788] ASoC: samsung: i2s: Request memory region in driver probe() The memory mapped registers region is common for both DAIs so request it in the I2S platform device driver's probe for the platform device corresponding to the primary DAI, rather than in the ASoC DAI's probe callback. While at it switch to devm_ioremap_resource(). This also drops the hard coded (0x100) register region size in the driver. Signed-off-by: Sylwester Nawrocki --- sound/soc/samsung/i2s.c | 45 +++++++---------------------------------- 1 file changed, 7 insertions(+), 38 deletions(-) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index aa52b41f174946..366b720731e117 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -59,10 +59,8 @@ struct samsung_i2s_dai_data { struct i2s_dai { /* Platform device for this DAI */ struct platform_device *pdev; - /* IOREMAP'd SFRs */ + /* Memory mapped SFR region */ void __iomem *addr; - /* Physical base address of SFRs */ - u32 base; /* Rate of RCLK source clock */ unsigned long rclk_srcrate; /* Frame Clock */ @@ -979,16 +977,9 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) goto probe_exit; } - i2s->addr = ioremap(i2s->base, 0x100); - if (i2s->addr == NULL) { - dev_err(&i2s->pdev->dev, "cannot ioremap registers\n"); - return -ENXIO; - } - i2s->clk = clk_get(&i2s->pdev->dev, "iis"); if (IS_ERR(i2s->clk)) { dev_err(&i2s->pdev->dev, "failed to get i2s_clock\n"); - iounmap(i2s->addr); return PTR_ERR(i2s->clk); } @@ -1001,7 +992,6 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) samsung_asoc_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture); if (other) { - other->addr = i2s->addr; other->clk = i2s->clk; } @@ -1043,8 +1033,6 @@ static int samsung_i2s_dai_remove(struct snd_soc_dai *dai) clk_disable_unprepare(i2s->clk); clk_put(i2s->clk); - - iounmap(i2s->addr); } i2s->clk = NULL; @@ -1162,7 +1150,6 @@ static int samsung_i2s_probe(struct platform_device *pdev) u32 regs_base, quirks = 0, idma_addr = 0; struct device_node *np = pdev->dev.of_node; const struct samsung_i2s_dai_data *i2s_dai_data; - int ret = 0; /* Call during Seconday interface registration */ i2s_dai_data = samsung_i2s_get_driver_data(pdev); @@ -1229,16 +1216,10 @@ static int samsung_i2s_probe(struct platform_device *pdev) } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "Unable to get I2S SFR address\n"); - return -ENXIO; - } + pri_dai->addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pri_dai->addr)) + return PTR_ERR(pri_dai->addr); - if (!request_mem_region(res->start, resource_size(res), - "samsung-i2s")) { - dev_err(&pdev->dev, "Unable to request SFR region\n"); - return -EBUSY; - } regs_base = res->start; pri_dai->dma_playback.dma_addr = regs_base + I2STXD; @@ -1247,7 +1228,6 @@ static int samsung_i2s_probe(struct platform_device *pdev) pri_dai->dma_capture.ch_name = "rx"; pri_dai->dma_playback.dma_size = 4; pri_dai->dma_capture.dma_size = 4; - pri_dai->base = regs_base; pri_dai->quirks = quirks; pri_dai->variant_regs = i2s_dai_data->i2s_variant_regs; @@ -1258,8 +1238,7 @@ static int samsung_i2s_probe(struct platform_device *pdev) sec_dai = i2s_alloc_dai(pdev, true); if (!sec_dai) { dev_err(&pdev->dev, "Unable to alloc I2S_sec\n"); - ret = -ENOMEM; - goto err; + return -ENOMEM; } sec_dai->variant_regs = pri_dai->variant_regs; @@ -1273,7 +1252,7 @@ static int samsung_i2s_probe(struct platform_device *pdev) } sec_dai->dma_playback.dma_size = 4; - sec_dai->base = regs_base; + sec_dai->addr = pri_dai->addr; sec_dai->quirks = quirks; sec_dai->idma_playback.dma_addr = idma_addr; sec_dai->pri_dai = pri_dai; @@ -1282,8 +1261,7 @@ static int samsung_i2s_probe(struct platform_device *pdev) if (i2s_pdata && i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) { dev_err(&pdev->dev, "Unable to configure gpio\n"); - ret = -EINVAL; - goto err; + return -EINVAL; } devm_snd_soc_register_component(&pri_dai->pdev->dev, @@ -1297,17 +1275,11 @@ static int samsung_i2s_probe(struct platform_device *pdev) return ret; return 0; -err: - if (res) - release_mem_region(regs_base, resource_size(res)); - - return ret; } static int samsung_i2s_remove(struct platform_device *pdev) { struct i2s_dai *i2s, *other; - struct resource *res; i2s = dev_get_drvdata(&pdev->dev); other = i2s->pri_dai ? : i2s->sec_dai; @@ -1317,9 +1289,6 @@ static int samsung_i2s_remove(struct platform_device *pdev) other->sec_dai = NULL; } else { pm_runtime_disable(&pdev->dev); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res) - release_mem_region(res->start, resource_size(res)); } i2s->pri_dai = NULL; From 59f7a28b64708a8b625ddbacd81da11eb6d91ff1 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 14 Jan 2015 19:42:31 +0100 Subject: [PATCH 603/788] ASoC: samsung: i2s: Move clk_get() to platform driver probe() Acquire the I2S interface clock in driver probe() callback as it's a per-device not a per-DAI clock. While at it switch to the resource managed clk_get(). Signed-off-by: Sylwester Nawrocki --- sound/soc/samsung/i2s.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 366b720731e117..a854ffca241608 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -971,18 +971,12 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; int ret; - if (other && other->clk) { /* If this is probe on secondary */ + if (is_secondary(i2s)) { /* If this is probe on the secondary DAI */ samsung_asoc_init_dma_data(dai, &other->sec_dai->dma_playback, NULL); goto probe_exit; } - i2s->clk = clk_get(&i2s->pdev->dev, "iis"); - if (IS_ERR(i2s->clk)) { - dev_err(&i2s->pdev->dev, "failed to get i2s_clock\n"); - return PTR_ERR(i2s->clk); - } - ret = clk_prepare_enable(i2s->clk); if (ret != 0) { dev_err(&i2s->pdev->dev, "failed to enable clock: %d\n", ret); @@ -991,10 +985,6 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) samsung_asoc_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture); - if (other) { - other->clk = i2s->clk; - } - if (i2s->quirks & QUIRK_NEED_RSTCLR) writel(CON_RSTCLR, i2s->addr + I2SCON); @@ -1032,7 +1022,6 @@ static int samsung_i2s_dai_remove(struct snd_soc_dai *dai) writel(0, i2s->addr + I2SCON); clk_disable_unprepare(i2s->clk); - clk_put(i2s->clk); } i2s->clk = NULL; @@ -1222,6 +1211,11 @@ static int samsung_i2s_probe(struct platform_device *pdev) regs_base = res->start; + pri_dai->clk = devm_clk_get(&pdev->dev, "iis"); + if (IS_ERR(pri_dai->clk)) { + dev_err(&pdev->dev, "Failed to get iis clock\n"); + return PTR_ERR(pri_dai->clk); + } pri_dai->dma_playback.dma_addr = regs_base + I2STXD; pri_dai->dma_capture.dma_addr = regs_base + I2SRXD; pri_dai->dma_playback.ch_name = "tx"; @@ -1253,6 +1247,7 @@ static int samsung_i2s_probe(struct platform_device *pdev) sec_dai->dma_playback.dma_size = 4; sec_dai->addr = pri_dai->addr; + sec_dai->clk = pri_dai->clk; sec_dai->quirks = quirks; sec_dai->idma_playback.dma_addr = idma_addr; sec_dai->pri_dai = pri_dai; From 2404f177331ba8be8dfc5d14247ca1c9a39bb5d9 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 14 Jan 2015 19:42:32 +0100 Subject: [PATCH 604/788] ASoC: samsung: i2s: Move clk enable to the platform driver probe() The clk_prepare_enable() call on the "iis" clock is moved to happen earlier in the DAI platform device driver's probe() callback, so the I2S registers can be safely accessed through the clk API, after the clk supplier is registered in the platform device probe(). After this patch the "iis" clock is kept enabled since the (primary) I2S platform device probe() and until the platform device driver remove() call. This is similar to gating the clock in the snd_soc_dai probe() and remove() callbacks. Normally, in addition to that we should mark the device as PM runtime active, so if runtime PM is enabled it can idle the device by turning off the clock. Correcting this issue is left for a separate patch series, as we need to ensure the BUSCLK clock is always enabled when required. Signed-off-by: Sylwester Nawrocki --- sound/soc/samsung/i2s.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index a854ffca241608..f75c19e5b5c3cb 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -969,7 +969,6 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) { struct i2s_dai *i2s = to_info(dai); struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; - int ret; if (is_secondary(i2s)) { /* If this is probe on the secondary DAI */ samsung_asoc_init_dma_data(dai, &other->sec_dai->dma_playback, @@ -977,12 +976,6 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) goto probe_exit; } - ret = clk_prepare_enable(i2s->clk); - if (ret != 0) { - dev_err(&i2s->pdev->dev, "failed to enable clock: %d\n", ret); - return ret; - } - samsung_asoc_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture); if (i2s->quirks & QUIRK_NEED_RSTCLR) @@ -1014,18 +1007,12 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) static int samsung_i2s_dai_remove(struct snd_soc_dai *dai) { struct i2s_dai *i2s = snd_soc_dai_get_drvdata(dai); - struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; - - if (!other || !other->clk) { + if (!is_secondary(i2s)) { if (i2s->quirks & QUIRK_NEED_RSTCLR) writel(0, i2s->addr + I2SCON); - - clk_disable_unprepare(i2s->clk); } - i2s->clk = NULL; - return 0; } @@ -1139,6 +1126,7 @@ static int samsung_i2s_probe(struct platform_device *pdev) u32 regs_base, quirks = 0, idma_addr = 0; struct device_node *np = pdev->dev.of_node; const struct samsung_i2s_dai_data *i2s_dai_data; + int ret; /* Call during Seconday interface registration */ i2s_dai_data = samsung_i2s_get_driver_data(pdev); @@ -1216,6 +1204,12 @@ static int samsung_i2s_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to get iis clock\n"); return PTR_ERR(pri_dai->clk); } + + ret = clk_prepare_enable(pri_dai->clk); + if (ret != 0) { + dev_err(&pdev->dev, "failed to enable clock: %d\n", ret); + return ret; + } pri_dai->dma_playback.dma_addr = regs_base + I2STXD; pri_dai->dma_capture.dma_addr = regs_base + I2SRXD; pri_dai->dma_playback.ch_name = "tx"; @@ -1286,6 +1280,9 @@ static int samsung_i2s_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } + if (!is_secondary(i2s)) + clk_disable_unprepare(i2s->clk); + i2s->pri_dai = NULL; i2s->sec_dai = NULL; From d1ad23dcdabe89bc7746f9a01fd1a7a09c92cb10 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 14 Jan 2015 19:42:33 +0100 Subject: [PATCH 605/788] ASoC: samsung: i2s: Add get_other_dai helper function The code to get pointer to the other DAI is repeated multiple times. Add a helper function and use it instead. Signed-off-by: Sylwester Nawrocki --- sound/soc/samsung/i2s.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index f75c19e5b5c3cb..cab2a2abc89b40 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -130,10 +130,16 @@ static inline bool tx_active(struct i2s_dai *i2s) return active ? true : false; } +/* Return pointer to the other DAI */ +static inline struct i2s_dai *get_other_dai(struct i2s_dai *i2s) +{ + return i2s->pri_dai ? : i2s->sec_dai; +} + /* If the other interface of the controller is transmitting data */ static inline bool other_tx_active(struct i2s_dai *i2s) { - struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; + struct i2s_dai *other = get_other_dai(i2s); return tx_active(other); } @@ -160,7 +166,7 @@ static inline bool rx_active(struct i2s_dai *i2s) /* If the other interface of the controller is receiving data */ static inline bool other_rx_active(struct i2s_dai *i2s) { - struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; + struct i2s_dai *other = get_other_dai(i2s); return rx_active(other); } @@ -461,7 +467,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, int dir) { struct i2s_dai *i2s = to_info(dai); - struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; + struct i2s_dai *other = get_other_dai(i2s); u32 mod = readl(i2s->addr + I2SMOD); const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs; unsigned int cdcon_mask = 1 << i2s_regs->cdclkcon_off; @@ -733,7 +739,7 @@ static int i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct i2s_dai *i2s = to_info(dai); - struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; + struct i2s_dai *other = get_other_dai(i2s); unsigned long flags; spin_lock_irqsave(&lock, flags); @@ -760,7 +766,7 @@ static void i2s_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct i2s_dai *i2s = to_info(dai); - struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; + struct i2s_dai *other = get_other_dai(i2s); unsigned long flags; const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs; @@ -791,7 +797,7 @@ static void i2s_shutdown(struct snd_pcm_substream *substream, static int config_setup(struct i2s_dai *i2s) { - struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; + struct i2s_dai *other = get_other_dai(i2s); unsigned rfs, bfs, blc; u32 psr; @@ -899,7 +905,7 @@ static int i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div) { struct i2s_dai *i2s = to_info(dai); - struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; + struct i2s_dai *other = get_other_dai(i2s); switch (div_id) { case SAMSUNG_I2S_DIV_BCLK: @@ -968,7 +974,7 @@ static int i2s_resume(struct snd_soc_dai *dai) static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) { struct i2s_dai *i2s = to_info(dai); - struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; + struct i2s_dai *other = get_other_dai(i2s); if (is_secondary(i2s)) { /* If this is probe on the secondary DAI */ samsung_asoc_init_dma_data(dai, &other->sec_dai->dma_playback, @@ -1271,7 +1277,7 @@ static int samsung_i2s_remove(struct platform_device *pdev) struct i2s_dai *i2s, *other; i2s = dev_get_drvdata(&pdev->dev); - other = i2s->pri_dai ? : i2s->sec_dai; + other = get_other_dai(i2s); if (other) { other->pri_dai = NULL; From 656ef288ef851b8feba839ff76f94a7ef859d999 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 14 Jan 2015 19:42:34 +0100 Subject: [PATCH 606/788] ASoC: samsung: i2s: Remove an unneeded goto usage The usage of this goto seems unjustified, use if/else statement instead. Signed-off-by: Sylwester Nawrocki --- sound/soc/samsung/i2s.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index cab2a2abc89b40..2bac71945b3e56 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -979,19 +979,18 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) if (is_secondary(i2s)) { /* If this is probe on the secondary DAI */ samsung_asoc_init_dma_data(dai, &other->sec_dai->dma_playback, NULL); - goto probe_exit; - } - - samsung_asoc_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture); + } else { + samsung_asoc_init_dma_data(dai, &i2s->dma_playback, + &i2s->dma_capture); - if (i2s->quirks & QUIRK_NEED_RSTCLR) - writel(CON_RSTCLR, i2s->addr + I2SCON); + if (i2s->quirks & QUIRK_NEED_RSTCLR) + writel(CON_RSTCLR, i2s->addr + I2SCON); - if (i2s->quirks & QUIRK_SUPPORTS_IDMA) - idma_reg_addr_init(i2s->addr, + if (i2s->quirks & QUIRK_SUPPORTS_IDMA) + idma_reg_addr_init(i2s->addr, i2s->sec_dai->idma_playback.dma_addr); + } -probe_exit: /* Reset any constraint on RFS and BFS */ i2s->rfs = 0; i2s->bfs = 0; From 51899576b2058fd824a55e8b9b9726b142eb3268 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 14 Jan 2015 19:42:35 +0100 Subject: [PATCH 607/788] ASoC: samsung: i2s: Add spinlock in place of local_irq_* calls It seems this driver hasn't been updated for SMP, as local_irq_save/ local_irq_restore don't provide proper protection of read/modify/write of the device's registers on such systems. Introduce a spinlock serializing access to the register region, it will be helpful later when I2SMOD, I2SPSR registers are made also accessible through the clk API. Signed-off-by: Sylwester Nawrocki --- sound/soc/samsung/i2s.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 2bac71945b3e56..20cc51fc76ae92 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -94,6 +94,10 @@ struct i2s_dai { u32 suspend_i2scon; u32 suspend_i2spsr; const struct samsung_i2s_variant_regs *variant_regs; + + /* Spinlock protecting access to the device's registers */ + spinlock_t spinlock; + spinlock_t *lock; }; /* Lock for cross i/f checks */ @@ -867,10 +871,10 @@ static int i2s_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - local_irq_save(flags); + spin_lock_irqsave(i2s->lock, flags); if (config_setup(i2s)) { - local_irq_restore(flags); + spin_unlock_irqrestore(i2s->lock, flags); return -EINVAL; } @@ -879,12 +883,12 @@ static int i2s_trigger(struct snd_pcm_substream *substream, else i2s_txctrl(i2s, 1); - local_irq_restore(flags); + spin_unlock_irqrestore(i2s->lock, flags); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - local_irq_save(flags); + spin_lock_irqsave(i2s->lock, flags); if (capture) { i2s_rxctrl(i2s, 0); @@ -894,7 +898,7 @@ static int i2s_trigger(struct snd_pcm_substream *substream, i2s_fifo(i2s, FIC_TXFLUSH); } - local_irq_restore(flags); + spin_unlock_irqrestore(i2s->lock, flags); break; } @@ -1157,6 +1161,9 @@ static int samsung_i2s_probe(struct platform_device *pdev) return -ENOMEM; } + spin_lock_init(&pri_dai->spinlock); + pri_dai->lock = &pri_dai->spinlock; + if (!np) { res = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (!res) { @@ -1234,6 +1241,7 @@ static int samsung_i2s_probe(struct platform_device *pdev) return -ENOMEM; } + sec_dai->lock = &pri_dai->spinlock; sec_dai->variant_regs = pri_dai->variant_regs; sec_dai->dma_playback.dma_addr = regs_base + I2STXDS; sec_dai->dma_playback.ch_name = "tx-sec"; From 8d7c4d6dde6bdc076c172b4683a2d9ff359b3cbd Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 14 Jan 2015 19:42:36 +0100 Subject: [PATCH 608/788] ASoC: samsung: i2s: Protect more registers with a spinlock Ensure the I2SMOD, I2SPSR registers, which are also exposed through clk API are only accessed with the i2s->spinlock spinlock held. Signed-off-by: Sylwester Nawrocki --- sound/soc/samsung/i2s.c | 81 ++++++++++++++++++++++++++--------------- 1 file changed, 51 insertions(+), 30 deletions(-) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 20cc51fc76ae92..05fc2f0e91cace 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -472,17 +472,22 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, { struct i2s_dai *i2s = to_info(dai); struct i2s_dai *other = get_other_dai(i2s); - u32 mod = readl(i2s->addr + I2SMOD); const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs; unsigned int cdcon_mask = 1 << i2s_regs->cdclkcon_off; unsigned int rsrc_mask = 1 << i2s_regs->rclksrc_off; + u32 mod, mask, val = 0; + + spin_lock(i2s->lock); + mod = readl(i2s->addr + I2SMOD); + spin_unlock(i2s->lock); switch (clk_id) { case SAMSUNG_I2S_OPCLK: - mod &= ~MOD_OPCLK_MASK; - mod |= dir; + mask = MOD_OPCLK_MASK; + val = dir; break; case SAMSUNG_I2S_CDCLK: + mask = 1 << i2s_regs->cdclkcon_off; /* Shouldn't matter in GATING(CLOCK_IN) mode */ if (dir == SND_SOC_CLOCK_IN) rfs = 0; @@ -499,15 +504,15 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, } if (dir == SND_SOC_CLOCK_IN) - mod |= 1 << i2s_regs->cdclkcon_off; - else - mod &= ~(1 << i2s_regs->cdclkcon_off); + val = 1 << i2s_regs->cdclkcon_off; i2s->rfs = rfs; break; case SAMSUNG_I2S_RCLKSRC_0: /* clock corrsponding to IISMOD[10] := 0 */ case SAMSUNG_I2S_RCLKSRC_1: /* clock corrsponding to IISMOD[10] := 1 */ + mask = 1 << i2s_regs->rclksrc_off; + if ((i2s->quirks & QUIRK_NO_MUXPSR) || (clk_id == SAMSUNG_I2S_RCLKSRC_0)) clk_id = 0; @@ -557,18 +562,19 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, return 0; } - if (clk_id == 0) - mod &= ~(1 << i2s_regs->rclksrc_off); - else - mod |= 1 << i2s_regs->rclksrc_off; - + if (clk_id == 1) + val = 1 << i2s_regs->rclksrc_off; break; default: dev_err(&i2s->pdev->dev, "We don't serve that!\n"); return -EINVAL; } + spin_lock(i2s->lock); + mod = readl(i2s->addr + I2SMOD); + mod = (mod & ~mask) | val; writel(mod, i2s->addr + I2SMOD); + spin_unlock(i2s->lock); return 0; } @@ -577,9 +583,8 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct i2s_dai *i2s = to_info(dai); - u32 mod = readl(i2s->addr + I2SMOD); int lrp_shift, sdf_shift, sdf_mask, lrp_rlow, mod_slave; - u32 tmp = 0; + u32 mod, tmp = 0; lrp_shift = i2s->variant_regs->lrp_off; sdf_shift = i2s->variant_regs->sdf_off; @@ -639,12 +644,15 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, return -EINVAL; } + spin_lock(i2s->lock); + mod = readl(i2s->addr + I2SMOD); /* * Don't change the I2S mode if any controller is active on this * channel. */ if (any_active(i2s) && ((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) { + spin_unlock(i2s->lock); dev_err(&i2s->pdev->dev, "%s:%d Other DAI busy\n", __func__, __LINE__); return -EAGAIN; @@ -653,6 +661,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, mod &= ~(sdf_mask | lrp_rlow | mod_slave); mod |= tmp; writel(mod, i2s->addr + I2SMOD); + spin_unlock(i2s->lock); return 0; } @@ -661,16 +670,16 @@ static int i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct i2s_dai *i2s = to_info(dai); - u32 mod = readl(i2s->addr + I2SMOD); + u32 mod, mask = 0, val = 0; if (!is_secondary(i2s)) - mod &= ~(MOD_DC2_EN | MOD_DC1_EN); + mask |= (MOD_DC2_EN | MOD_DC1_EN); switch (params_channels(params)) { case 6: - mod |= MOD_DC2_EN; + val |= MOD_DC2_EN; case 4: - mod |= MOD_DC1_EN; + val |= MOD_DC1_EN; break; case 2: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) @@ -692,44 +701,49 @@ static int i2s_hw_params(struct snd_pcm_substream *substream, } if (is_secondary(i2s)) - mod &= ~MOD_BLCS_MASK; + mask |= MOD_BLCS_MASK; else - mod &= ~MOD_BLCP_MASK; + mask |= MOD_BLCP_MASK; if (is_manager(i2s)) - mod &= ~MOD_BLC_MASK; + mask |= MOD_BLC_MASK; switch (params_width(params)) { case 8: if (is_secondary(i2s)) - mod |= MOD_BLCS_8BIT; + val |= MOD_BLCS_8BIT; else - mod |= MOD_BLCP_8BIT; + val |= MOD_BLCP_8BIT; if (is_manager(i2s)) - mod |= MOD_BLC_8BIT; + val |= MOD_BLC_8BIT; break; case 16: if (is_secondary(i2s)) - mod |= MOD_BLCS_16BIT; + val |= MOD_BLCS_16BIT; else - mod |= MOD_BLCP_16BIT; + val |= MOD_BLCP_16BIT; if (is_manager(i2s)) - mod |= MOD_BLC_16BIT; + val |= MOD_BLC_16BIT; break; case 24: if (is_secondary(i2s)) - mod |= MOD_BLCS_24BIT; + val |= MOD_BLCS_24BIT; else - mod |= MOD_BLCP_24BIT; + val |= MOD_BLCP_24BIT; if (is_manager(i2s)) - mod |= MOD_BLC_24BIT; + val |= MOD_BLC_24BIT; break; default: dev_err(&i2s->pdev->dev, "Format(%d) not supported\n", params_format(params)); return -EINVAL; } + + spin_lock(i2s->lock); + mod = readl(i2s->addr + I2SMOD); + mod = (mod & ~mask) | val; writel(mod, i2s->addr + I2SMOD); + spin_unlock(i2s->lock); samsung_asoc_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture); @@ -979,6 +993,7 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) { struct i2s_dai *i2s = to_info(dai); struct i2s_dai *other = get_other_dai(i2s); + unsigned long flags; if (is_secondary(i2s)) { /* If this is probe on the secondary DAI */ samsung_asoc_init_dma_data(dai, &other->sec_dai->dma_playback, @@ -999,11 +1014,14 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) i2s->rfs = 0; i2s->bfs = 0; i2s->rclk_srcrate = 0; + + spin_lock_irqsave(i2s->lock, flags); i2s_txctrl(i2s, 0); i2s_rxctrl(i2s, 0); i2s_fifo(i2s, FIC_TXFLUSH); i2s_fifo(other, FIC_TXFLUSH); i2s_fifo(i2s, FIC_RXFLUSH); + spin_unlock_irqrestore(i2s->lock, flags); /* Gate CDCLK by default */ if (!is_opened(other)) @@ -1018,8 +1036,11 @@ static int samsung_i2s_dai_remove(struct snd_soc_dai *dai) struct i2s_dai *i2s = snd_soc_dai_get_drvdata(dai); if (!is_secondary(i2s)) { - if (i2s->quirks & QUIRK_NEED_RSTCLR) + if (i2s->quirks & QUIRK_NEED_RSTCLR) { + spin_lock(i2s->lock); writel(0, i2s->addr + I2SCON); + spin_unlock(i2s->lock); + } } return 0; From 4e0764ed1d9a452663160580e0d30af8e3a83325 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 14 Jan 2015 19:42:37 +0100 Subject: [PATCH 609/788] ASoC: samsung: odroidx2: Handle I2S CDCLK clock conditionally In order to support old DTs we check the codec device node if it contains "clocks" property and only if it doesn't (which indicates an old DT) we proceed with enabling the CDCLK clock by means of the set_sysclk() callback. For new DTs which use the common clock bindings for CDCLK that clock is supposed to be handled outside the sound machine driver. Signed-off-by: Sylwester Nawrocki --- sound/soc/samsung/odroidx2_max98090.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sound/soc/samsung/odroidx2_max98090.c b/sound/soc/samsung/odroidx2_max98090.c index fa4f1d2f69bfa0..596f1180a36986 100644 --- a/sound/soc/samsung/odroidx2_max98090.c +++ b/sound/soc/samsung/odroidx2_max98090.c @@ -21,6 +21,8 @@ struct odroidx2_drv_data { /* The I2S CDCLK output clock frequency for the MAX98090 codec */ #define MAX98090_MCLK 19200000 +static struct snd_soc_dai_link odroidx2_dai[]; + static int odroidx2_late_probe(struct snd_soc_card *card) { struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai; @@ -29,7 +31,9 @@ static int odroidx2_late_probe(struct snd_soc_card *card) ret = snd_soc_dai_set_sysclk(codec_dai, 0, MAX98090_MCLK, SND_SOC_CLOCK_IN); - if (ret < 0) + + if (ret < 0 || of_find_property(odroidx2_dai[0].codec_of_node, + "clocks", NULL)) return ret; /* Set the cpu DAI configuration in order to use CDCLK */ From f7f09630868bc4eab556ee26ba409e610e961699 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 14 Jan 2015 19:42:38 +0100 Subject: [PATCH 610/788] ASoC: samsung: i2s: Add clk provider DT binding documentation The new DT properties required for the I2S device node to be referred as a clock provider and corresponding clock indices definition is added. Signed-off-by: Sylwester Nawrocki --- .../devicetree/bindings/sound/samsung-i2s.txt | 22 +++++++++++++++++++ include/dt-bindings/sound/samsung-i2s.h | 8 +++++++ 2 files changed, 30 insertions(+) create mode 100644 include/dt-bindings/sound/samsung-i2s.h diff --git a/Documentation/devicetree/bindings/sound/samsung-i2s.txt b/Documentation/devicetree/bindings/sound/samsung-i2s.txt index d188296bb6ec30..09e0e18591ae26 100644 --- a/Documentation/devicetree/bindings/sound/samsung-i2s.txt +++ b/Documentation/devicetree/bindings/sound/samsung-i2s.txt @@ -33,6 +33,25 @@ Required SoC Specific Properties: "iis" is the i2s bus clock and i2s_opclk0, i2s_opclk1 are sources of the root clk. i2s0 has internal mux to select the source of root clk and i2s1 and i2s2 doesn't have any such mux. +- #clock-cells: should be 1, this property must be present if the I2S device + is a clock provider in terms of the common clock bindings, described in + ../clock/clock-bindings.txt. +- clock-output-names: from the common clock bindings, names of the CDCLK + I2S output clocks, suggested values are "i2s_cdclk0", "i2s_cdclk1", + "i2s_cdclk3" for the I2S0, I2S1, I2S2 devices recpectively. + +There are following clocks available at the I2S device nodes: + CLK_I2S_CDCLK - the CDCLK (CODECLKO) gate clock, + CLK_I2S_RCLK_PSR - the RCLK prescaler divider clock (corresponding to the + IISPSR register), + CLK_I2S_RCLK_SRC - the RCLKSRC mux clock (corresponding to RCLKSRC bit in + IISMOD register). + +Refer to the SoC datasheet for availability of the above clocks. +The CLK_I2S_RCLK_PSR and CLK_I2S_RCLK_SRC clocks are usually only available +in the IIS Multi Audio Interface (I2S0). +Note: Old DTs may not have the #clock-cells, clock-output-names properties +and then not use the I2S node as a clock supplier. Optional SoC Specific Properties: @@ -41,6 +60,7 @@ Optional SoC Specific Properties: - pinctrl-0: Should specify pin control groups used for this controller. - pinctrl-names: Should contain only one value - "default". + Example: i2s0: i2s@03830000 { @@ -54,6 +74,8 @@ i2s0: i2s@03830000 { <&clock_audss EXYNOS_I2S_BUS>, <&clock_audss EXYNOS_SCLK_I2S>; clock-names = "iis", "i2s_opclk0", "i2s_opclk1"; + #clock-cells; + clock-output-names = "i2s_cdclk0"; samsung,idma-addr = <0x03000000>; pinctrl-names = "default"; pinctrl-0 = <&i2s0_bus>; diff --git a/include/dt-bindings/sound/samsung-i2s.h b/include/dt-bindings/sound/samsung-i2s.h new file mode 100644 index 00000000000000..0c69818d530ce8 --- /dev/null +++ b/include/dt-bindings/sound/samsung-i2s.h @@ -0,0 +1,8 @@ +#ifndef _DT_BINDINGS_SAMSUNG_I2S_H +#define _DT_BINDINGS_SAMSUNG_I2S_H + +#define CLK_I2S_CDCLK 0 +#define CLK_I2S_RCLK_SRC 1 +#define CLK_I2S_RCLK_PSR 2 + +#endif /* _DT_BINDINGS_SAMSUNG_I2S_H */ From fa3160bf8a66d46b4cbb0d17b186c3183d4676b3 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 14 Jan 2015 19:42:39 +0100 Subject: [PATCH 611/788] ASoC: samsung: i2s: Add clock provider for the I2S internal clocks This patch adds clock provider (currently only for DT platforms) for the CODECLKO (CDCLK) gate, RCLKSRC mux and RCLK pre-scaler divider divider clock. Those all tree clock are only available in the IIS Multi Audio Interface (I2S0), the regular IIS Bus Interface has only CDCLK gate clock. The motivation behind this patch is to expose the I2S internal clocks which are currently controlled through set_sysclk() through the clk API, so dedicated sound machine driver per each board can be avoided. The intention is also to fix the CDCLK gating issue reported by Daniel Drake: http://mailman.alsa-project.org/pipermail/alsa-devel/2014-September/081753.html This patch also reverts commit b97c60abf9a561f86ae71bd741add02673cc1 ("ASoC: samsung-i2s: Maintain CDCLK settings across i2s_{shutdown/ startup}") The problem that commit attempted to solve only affects the Odroid X2/U3, which doesn't configure the CDCLK clock in struct snd_soc_dai_ops hw_params callback and the issue should be now resolved by using clk API, i.e. having the codec enabling/ disabling the CDCLK clock as required. Signed-off-by: Sylwester Nawrocki --- sound/soc/samsung/i2s.c | 113 +++++++++++++++++++++++++++++++++------- 1 file changed, 93 insertions(+), 20 deletions(-) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 05fc2f0e91cace..b92ab40d2be6eb 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -10,9 +10,11 @@ * published by the Free Software Foundation. */ +#include #include #include #include +#include #include #include #include @@ -81,8 +83,6 @@ struct i2s_dai { #define DAI_OPENED (1 << 0) /* Dai is opened */ #define DAI_MANAGER (1 << 1) /* Dai is the manager */ unsigned mode; - /* CDCLK pin direction: 0 - input, 1 - output */ - unsigned int cdclk_out:1; /* Driver for this DAI */ struct snd_soc_dai_driver i2s_dai_drv; /* DMA parameters */ @@ -98,6 +98,10 @@ struct i2s_dai { /* Spinlock protecting access to the device's registers */ spinlock_t spinlock; spinlock_t *lock; + + /* Below fields are only valid if this is the primary FIFO */ + struct clk *clk_table[3]; + struct clk_onecell_data clk_data; }; /* Lock for cross i/f checks */ @@ -774,9 +778,6 @@ static int i2s_startup(struct snd_pcm_substream *substream, spin_unlock_irqrestore(&lock, flags); - if (!is_opened(other) && i2s->cdclk_out) - i2s_set_sysclk(dai, SAMSUNG_I2S_CDCLK, - 0, SND_SOC_CLOCK_OUT); return 0; } @@ -786,31 +787,20 @@ static void i2s_shutdown(struct snd_pcm_substream *substream, struct i2s_dai *i2s = to_info(dai); struct i2s_dai *other = get_other_dai(i2s); unsigned long flags; - const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs; spin_lock_irqsave(&lock, flags); i2s->mode &= ~DAI_OPENED; i2s->mode &= ~DAI_MANAGER; - if (is_opened(other)) { + if (is_opened(other)) other->mode |= DAI_MANAGER; - } else { - u32 mod = readl(i2s->addr + I2SMOD); - i2s->cdclk_out = !(mod & (1 << i2s_regs->cdclkcon_off)); - if (other) - other->cdclk_out = i2s->cdclk_out; - } + /* Reset any constraint on RFS and BFS */ i2s->rfs = 0; i2s->bfs = 0; spin_unlock_irqrestore(&lock, flags); - - /* Gate CDCLK by default */ - if (!is_opened(other)) - i2s_set_sysclk(dai, SAMSUNG_I2S_CDCLK, - 0, SND_SOC_CLOCK_IN); } static int config_setup(struct i2s_dai *i2s) @@ -1147,6 +1137,87 @@ static int i2s_runtime_resume(struct device *dev) } #endif /* CONFIG_PM */ +static void i2s_unregister_clocks(struct i2s_dai *i2s) +{ + int i; + + for (i = 0; i < i2s->clk_data.clk_num; i++) { + if (!IS_ERR(i2s->clk_table[i])) + clk_unregister(i2s->clk_table[i]); + } +} + +static void i2s_unregister_clock_provider(struct platform_device *pdev) +{ + struct i2s_dai *i2s = dev_get_drvdata(&pdev->dev); + + of_clk_del_provider(pdev->dev.of_node); + i2s_unregister_clocks(i2s); +} + +static int i2s_register_clock_provider(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct i2s_dai *i2s = dev_get_drvdata(dev); + const char *clk_name[2] = { "i2s_opclk0", "i2s_opclk1" }; + const char *p_names[2] = { NULL }; + const struct samsung_i2s_variant_regs *reg_info = i2s->variant_regs; + struct clk *rclksrc; + int ret, i; + + /* Register the clock provider only if it's expected in the DTB */ + if (!of_find_property(dev->of_node, "#clock-cells", NULL)) + return 0; + + /* Get the RCLKSRC mux clock parent clock names */ + for (i = 0; i < ARRAY_SIZE(p_names); i++) { + rclksrc = clk_get(dev, clk_name[i]); + if (IS_ERR(rclksrc)) + continue; + p_names[i] = __clk_get_name(rclksrc); + clk_put(rclksrc); + } + + if (!(i2s->quirks & QUIRK_NO_MUXPSR)) { + /* Activate the prescaler */ + u32 val = readl(i2s->addr + I2SPSR); + writel(val | PSR_PSREN, i2s->addr + I2SPSR); + + i2s->clk_table[CLK_I2S_RCLK_SRC] = clk_register_mux(NULL, + "i2s_rclksrc", p_names, ARRAY_SIZE(p_names), + CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, + i2s->addr + I2SMOD, reg_info->rclksrc_off, + 1, 0, i2s->lock); + + i2s->clk_table[CLK_I2S_RCLK_PSR] = clk_register_divider(NULL, + "i2s_presc", "i2s_rclksrc", + CLK_SET_RATE_PARENT, + i2s->addr + I2SPSR, 8, 6, 0, i2s->lock); + + p_names[0] = "i2s_presc"; + i2s->clk_data.clk_num = 2; + } + of_property_read_string_index(dev->of_node, + "clock-output-names", 0, &clk_name[0]); + + i2s->clk_table[CLK_I2S_CDCLK] = clk_register_gate(NULL, clk_name[0], + p_names[0], CLK_SET_RATE_PARENT, + i2s->addr + I2SMOD, reg_info->cdclkcon_off, + CLK_GATE_SET_TO_DISABLE, i2s->lock); + + i2s->clk_data.clk_num += 1; + i2s->clk_data.clks = i2s->clk_table; + + ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, + &i2s->clk_data); + if (ret < 0) { + dev_err(dev, "failed to add clock provider: %d\n", ret); + i2s_unregister_clocks(i2s); + } + + return ret; +} + static int samsung_i2s_probe(struct platform_device *pdev) { struct i2s_dai *pri_dai, *sec_dai = NULL; @@ -1297,7 +1368,7 @@ static int samsung_i2s_probe(struct platform_device *pdev) if (ret != 0) return ret; - return 0; + return i2s_register_clock_provider(pdev); } static int samsung_i2s_remove(struct platform_device *pdev) @@ -1314,8 +1385,10 @@ static int samsung_i2s_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } - if (!is_secondary(i2s)) + if (!is_secondary(i2s)) { + i2s_unregister_clock_provider(pdev); clk_disable_unprepare(i2s->clk); + } i2s->pri_dai = NULL; i2s->sec_dai = NULL; From 99f7df6896621a7aa67bcbfafd430419cc379077 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 14 Jan 2015 19:42:40 +0100 Subject: [PATCH 612/788] ARM: dts: Exynos4 and Odroid X2/U3 sound device nodes update Clock related properties are added to the Exynos4 I2S device nodes so they can be referred to as clock providers. Missing i2s_opclk1 clock is added to the I2S0 node and clock properties are added to the MAX98090 codec node to allow it to control/read frequency of the MCLK clock directly. Signed-off-by: Sylwester Nawrocki --- arch/arm/boot/dts/exynos4.dtsi | 6 ++++++ arch/arm/boot/dts/exynos4412-odroid-common.dtsi | 8 ++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi index c4d80bb91c321a..41b9c785df7065 100644 --- a/arch/arm/boot/dts/exynos4.dtsi +++ b/arch/arm/boot/dts/exynos4.dtsi @@ -62,6 +62,8 @@ reg = <0x03830000 0x100>; clocks = <&clock_audss EXYNOS_I2S_BUS>; clock-names = "iis"; + #clock-cells = <1>; + clock-output-names = "i2s_cdclk0"; dmas = <&pdma0 12>, <&pdma0 11>, <&pdma0 10>; dma-names = "tx", "rx", "tx-sec"; samsung,idma-addr = <0x03000000>; @@ -381,6 +383,8 @@ reg = <0x13960000 0x100>; clocks = <&clock CLK_I2S1>; clock-names = "iis"; + #clock-cells = <1>; + clock-output-names = "i2s_cdclk1"; dmas = <&pdma1 12>, <&pdma1 11>; dma-names = "tx", "rx"; status = "disabled"; @@ -391,6 +395,8 @@ reg = <0x13970000 0x100>; clocks = <&clock CLK_I2S2>; clock-names = "iis"; + #clock-cells = <1>; + clock-output-names = "i2s_cdclk2"; dmas = <&pdma0 14>, <&pdma0 13>; dma-names = "tx", "rx"; status = "disabled"; diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index e10efa88807a9a..ec1015051685c4 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -7,6 +7,7 @@ * published by the Free Software Foundation. */ +#include #include #include "exynos4412.dtsi" @@ -37,8 +38,9 @@ pinctrl-names = "default"; status = "okay"; clocks = <&clock_audss EXYNOS_I2S_BUS>, - <&clock_audss EXYNOS_DOUT_AUD_BUS>; - clock-names = "iis", "i2s_opclk0"; + <&clock_audss EXYNOS_DOUT_AUD_BUS>, + <&clock_audss EXYNOS_SCLK_I2S>; + clock-names = "iis", "i2s_opclk0", "i2s_opclk1"; }; sound: sound { @@ -387,6 +389,8 @@ reg = <0x10>; interrupt-parent = <&gpx0>; interrupts = <0 0>; + clocks = <&i2s0 CLK_I2S_CDCLK>; + clock-names = "mclk"; }; }; From f291353ac916eb63c2b00e658fea470dcd1b1b9c Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 14 Jan 2015 19:42:41 +0100 Subject: [PATCH 613/788] ARM: dts: Switch Odroid X2/U2 to simple-audio-card Now when the CDCLK I2S output clock can be handled through the clock API the Odroid X2/U3 can be switched to the simple-audio-card DT binding. Signed-off-by: Sylwester Nawrocki --- arch/arm/boot/dts/exynos4.dtsi | 3 +++ .../boot/dts/exynos4412-odroid-common.dtsi | 19 ++++++++++++++++--- arch/arm/boot/dts/exynos4412-odroidu3.dts | 8 +++++--- arch/arm/boot/dts/exynos4412-odroidx2.dts | 8 ++++++-- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi index 41b9c785df7065..04641262912d38 100644 --- a/arch/arm/boot/dts/exynos4.dtsi +++ b/arch/arm/boot/dts/exynos4.dtsi @@ -67,6 +67,7 @@ dmas = <&pdma0 12>, <&pdma0 11>, <&pdma0 10>; dma-names = "tx", "rx", "tx-sec"; samsung,idma-addr = <0x03000000>; + #sound-dai-cells = <1>; status = "disabled"; }; @@ -387,6 +388,7 @@ clock-output-names = "i2s_cdclk1"; dmas = <&pdma1 12>, <&pdma1 11>; dma-names = "tx", "rx"; + #sound-dai-cells = <1>; status = "disabled"; }; @@ -399,6 +401,7 @@ clock-output-names = "i2s_cdclk2"; dmas = <&pdma0 14>, <&pdma0 13>; dma-names = "tx", "rx"; + #sound-dai-cells = <1>; status = "disabled"; }; diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index ec1015051685c4..456d26a6164a8e 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -44,9 +44,7 @@ }; sound: sound { - compatible = "samsung,odroidx2-audio"; - samsung,i2s-controller = <&i2s0>; - samsung,audio-codec = <&max98090>; + compatible = "simple-audio-card"; assigned-clocks = <&clock_audss EXYNOS_MOUT_AUDSS>, <&clock_audss EXYNOS_MOUT_I2S>, <&clock_audss EXYNOS_DOUT_SRP>, @@ -57,6 +55,20 @@ <0>, <192000000>, <19200000>; + + simple-audio-card,format = "i2s"; + simple-audio-card,bitclock-master = <&link0_codec>; + simple-audio-card,frame-master = <&link0_codec>; + + simple-audio-card,cpu { + sound-dai = <&i2s0 0>; + system-clock-frequency = <19200000>; + }; + + link0_codec: simple-audio-card,codec { + sound-dai = <&max98090>; + clocks = <&i2s0 CLK_I2S_CDCLK>; + }; }; mmc@12550000 { @@ -391,6 +403,7 @@ interrupts = <0 0>; clocks = <&i2s0 CLK_I2S_CDCLK>; clock-names = "mclk"; + #sound-dai-cells = <0>; }; }; diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts b/arch/arm/boot/dts/exynos4412-odroidu3.dts index 973349b9c413b1..03bdb46244350d 100644 --- a/arch/arm/boot/dts/exynos4412-odroidu3.dts +++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts @@ -63,9 +63,11 @@ }; &sound { - compatible = "samsung,odroidu3-audio"; - samsung,model = "Odroid-U3"; - samsung,audio-routing = + simple-audio-card,name = "Odroid-U3"; + simple-audio-card,widgets = + "Headphone", "Headphone Jack", + "Speakers", "Speakers"; + simple-audio-card,routing = "Headphone Jack", "HPL", "Headphone Jack", "HPR", "Headphone Jack", "MICBIAS", diff --git a/arch/arm/boot/dts/exynos4412-odroidx2.dts b/arch/arm/boot/dts/exynos4412-odroidx2.dts index 96b43f4497cc0e..6e33678562aebf 100644 --- a/arch/arm/boot/dts/exynos4412-odroidx2.dts +++ b/arch/arm/boot/dts/exynos4412-odroidx2.dts @@ -23,8 +23,12 @@ }; &sound { - samsung,model = "Odroid-X2"; - samsung,audio-routing = + simple-audio-card,name = "Odroid-X2"; + simple-audio-card,widgets = + "Headphone", "Headphone Jack", + "Microphone", "Mic Jack", + "Microphone", "DMIC"; + simple-audio-card,routing = "Headphone Jack", "HPL", "Headphone Jack", "HPR", "IN1", "Mic Jack", From 9a279d312cd2039f17266665ed6dd16364b8fafd Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Wed, 14 Jan 2015 20:43:42 +0100 Subject: [PATCH 614/788] patchset: [PATCH V3 00/15] ASoC: samsung: Add clk provider for I2S internal clocks --- PATCHES | 1 + 1 file changed, 1 insertion(+) diff --git a/PATCHES b/PATCHES index fbfc637d588287..12f39a5d04ba24 100644 --- a/PATCHES +++ b/PATCHES @@ -1,2 +1,3 @@ [PATCH v12 0/9] Enable L2 cache support on Exynos4210/4x12 SoCs [PATCH v2 0/6] Enable HDMI support on Exynos platforms +[PATCH V3 00/15] ASoC: samsung: Add clk provider for I2S internal clocks From 8f697746463274717d24f2efd5ccc54f15f3de9f Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 12 Jan 2015 21:34:49 +0900 Subject: [PATCH 615/788] devfreq: event: Add new devfreq_event class to provide basic data for devfreq governor This patch add new devfreq_event class for devfreq_event device which provide raw data (e.g., memory bus utilization/GPU utilization). This raw data from devfreq_event data would be used for the governor of devfreq subsystem. - devfreq_event device : Provide raw data for governor of existing devfreq device - devfreq device : Monitor device state and change frequency/voltage of device using the raw data from devfreq_event device The devfreq subsystem support generic DVFS(Dynamic Voltage/Frequency Scaling) for Non-CPU Devices. The devfreq device would dertermine current device state using various governor (e.g., ondemand, performance, powersave). After completed determination of system state, devfreq device would change the frequency/voltage of devfreq device according to the result of governor. But, devfreq governor must need basic data which indicates current device state. Existing devfreq subsystem only consider devfreq device which check current system state and determine proper system state using basic data. There is no subsystem for device providing basic data to devfreq device. The devfreq subsystem must need devfreq_event device(data-provider device) for existing devfreq device. So, this patch add new devfreq_event class for devfreq_event device which read various basic data(e.g, memory bus utilization, GPU utilization) and provide measured data to existing devfreq device through standard APIs of devfreq_event class. The following description explains the feature of two kind of devfreq class: - devfreq class (existing) : devfreq consumer device use raw data from devfreq_event device for determining proper current system state and change voltage/frequency dynamically using various governors. - devfreq_event class (new) : Provide measured raw data to devfreq device for governor Cc: MyungJoo Ham Cc: Kyungmin Park Signed-off-by: Chanwoo Choi --- drivers/devfreq/Kconfig | 2 + drivers/devfreq/Makefile | 6 +- drivers/devfreq/devfreq-event.c | 448 ++++++++++++++++++++++++++++++++ drivers/devfreq/event/Kconfig | 16 ++ drivers/devfreq/event/Makefile | 1 + include/linux/devfreq-event.h | 170 ++++++++++++ 6 files changed, 642 insertions(+), 1 deletion(-) create mode 100644 drivers/devfreq/devfreq-event.c create mode 100644 drivers/devfreq/event/Kconfig create mode 100644 drivers/devfreq/event/Makefile create mode 100644 include/linux/devfreq-event.h diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index faf4e70c42e046..21f8f17aca5f18 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -87,4 +87,6 @@ config ARM_EXYNOS5_BUS_DEVFREQ It reads PPMU counters of memory controllers and adjusts the operating frequencies and voltages with OPP support. +source "drivers/devfreq/event/Kconfig" + endif # PM_DEVFREQ diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index 16138c9e0d5875..c449336ed56502 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -1,4 +1,5 @@ -obj-$(CONFIG_PM_DEVFREQ) += devfreq.o +obj-$(CONFIG_PM_DEVFREQ) += devfreq.o +obj-$(CONFIG_PM_DEVFREQ_EVENT) += devfreq-event.o obj-$(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND) += governor_simpleondemand.o obj-$(CONFIG_DEVFREQ_GOV_PERFORMANCE) += governor_performance.o obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE) += governor_powersave.o @@ -7,3 +8,6 @@ obj-$(CONFIG_DEVFREQ_GOV_USERSPACE) += governor_userspace.o # DEVFREQ Drivers obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ) += exynos/ obj-$(CONFIG_ARM_EXYNOS5_BUS_DEVFREQ) += exynos/ + +# DEVFREQ Event Drivers +obj-$(CONFIG_PM_DEVFREQ_EVENT) += event/ diff --git a/drivers/devfreq/devfreq-event.c b/drivers/devfreq/devfreq-event.c new file mode 100644 index 00000000000000..9ba5ba41792e9b --- /dev/null +++ b/drivers/devfreq/devfreq-event.c @@ -0,0 +1,448 @@ +/* + * devfreq-event: a framework to provide raw data and events of devfreq devices + * + * Copyright (C) 2014 Samsung Electronics + * Author: Chanwoo Choi + * + * 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 driver is based on drivers/devfreq/devfreq.c. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct class *devfreq_event_class; + +/* The list of all devfreq event list */ +static LIST_HEAD(devfreq_event_list); +static DEFINE_MUTEX(devfreq_event_list_lock); + +#define to_devfreq_event(DEV) container_of(DEV, struct devfreq_event_dev, dev) + +/** + * devfreq_event_enable_edev() - Enable the devfreq-event dev and increase + * the enable_count of devfreq-event dev. + * @edev : the devfreq-event device + * + * Note that this function increase the enable_count and enable the + * devfreq-event device. The devfreq-event device should be enabled before + * using it by devfreq device. + */ +int devfreq_event_enable_edev(struct devfreq_event_dev *edev) +{ + int ret = 0; + + if (!edev || !edev->desc) + return -EINVAL; + + mutex_lock(&edev->lock); + if (edev->desc->ops && edev->desc->ops->enable) { + ret = edev->desc->ops->enable(edev); + if (ret < 0) + goto err; + } + edev->enable_count++; +err: + mutex_unlock(&edev->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(devfreq_event_enable_edev); + +/** + * devfreq_event_disable_edev() - Disable the devfreq-event dev and decrease + * the enable_count of the devfreq-event dev. + * @edev : the devfreq-event device + * + * Note that this function decrease the enable_count and disable the + * devfreq-event device. After the devfreq-event device is disabled, + * devfreq device can't use the devfreq-event device for get/set/reset + * operations. + */ +int devfreq_event_disable_edev(struct devfreq_event_dev *edev) +{ + int ret = 0; + + if (!edev || !edev->desc) + return -EINVAL; + + mutex_lock(&edev->lock); + if (edev->enable_count > 0) { + edev->enable_count--; + } else { + dev_warn(&edev->dev, "unbalanced enable_count\n"); + ret = -EINVAL; + goto err; + } + + if (edev->desc->ops && edev->desc->ops->disable) { + ret = edev->desc->ops->disable(edev); + if (ret < 0) { + edev->enable_count++; + goto err; + } + } +err: + mutex_unlock(&edev->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(devfreq_event_disable_edev); + +/** + * devfreq_event_is_enabled() - Check whether devfreq-event dev is enabled or + * not. + * @edev : the devfreq-event device + * + * Note that this function check whether devfreq-event dev is enabled or not. + * If return true, the devfreq-event dev is enabeld. If return false, the + * devfreq-event dev is disabled. + */ +bool devfreq_event_is_enabled(struct devfreq_event_dev *edev) +{ + bool enabled = false; + + if (!edev || !edev->desc) + return enabled; + + mutex_lock(&edev->lock); + + if (edev->enable_count > 0) + enabled = true; + + mutex_unlock(&edev->lock); + + return enabled; +} +EXPORT_SYMBOL_GPL(devfreq_event_is_enabled); + +/** + * devfreq_event_set_event() - Set event to devfreq-event dev to start. + * @edev : the devfreq-event device + * + * Note that this function set the event to the devfreq-event device to start + * for getting the event data which could be various event type. + */ +int devfreq_event_set_event(struct devfreq_event_dev *edev) +{ + int ret; + + if (!edev || !edev->desc) + return -EINVAL; + + if (!edev->desc->ops || !edev->desc->ops->set_event) + return -EINVAL; + + if (!devfreq_event_is_enabled(edev)) + return -EPERM; + + mutex_lock(&edev->lock); + ret = edev->desc->ops->set_event(edev); + mutex_unlock(&edev->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(devfreq_event_set_event); + +/** + * devfreq_event_get_event() - Get event and total_event from devfreq-event dev. + * @edev : the devfreq-event device + * @edata : the calculated data of devfreq-event device + * + * Note that this function get the calculated event data from devfreq-event dev + * after stoping the progress of whole sequence of devfreq-event dev. + */ +int devfreq_event_get_event(struct devfreq_event_dev *edev, + struct devfreq_event_data *edata) +{ + int ret; + + if (!edev || !edev->desc) + return -EINVAL; + + if (!edev->desc->ops || !edev->desc->ops->get_event) + return -EINVAL; + + if (!devfreq_event_is_enabled(edev)) + return -EINVAL; + + edata->event = edata->total_event = 0; + + mutex_lock(&edev->lock); + ret = edev->desc->ops->get_event(edev, edata); + mutex_unlock(&edev->lock); + + if ((edata->total_event <= 0) + || (edata->event > edata->total_event)) { + edata->event = edata->total_event = 0; + ret = -EINVAL; + } + + return ret; +} +EXPORT_SYMBOL_GPL(devfreq_event_get_event); + +/** + * devfreq_event_reset_event() - Reset all opeations of devfreq-event dev. + * @edev : the devfreq-event device + * + * Note that this function stop all operations of devfreq-event dev and reset + * the current event data to make the devfreq-event device into initial state. + */ +int devfreq_event_reset_event(struct devfreq_event_dev *edev) +{ + int ret = 0; + + if (!edev || !edev->desc) + return -EINVAL; + + if (!devfreq_event_is_enabled(edev)) + return -EPERM; + + mutex_lock(&edev->lock); + if (edev->desc->ops && edev->desc->ops->reset) + ret = edev->desc->ops->reset(edev); + mutex_unlock(&edev->lock); + + if (ret < 0) + return -EINVAL; + + return ret; +} +EXPORT_SYMBOL_GPL(devfreq_event_reset_event); + +/** + * devfreq_event_get_drvdata() - Return driver-data of devfreq-event dev. + * @edev : the devfreq-event device + * + * Note that this function return the driver-data of devfreq-event device. + */ +void *devfreq_event_get_drvdata(struct devfreq_event_dev *edev) +{ + return edev->desc->driver_data; +} +EXPORT_SYMBOL_GPL(devfreq_event_get_drvdata); + +/** + * devfreq_event_get_edev_by_phandle() - Get the devfreq-event dev from + * devicetree. + * @dev : the pointer to the given device + * @index : the index into list of devfreq-event device + * + * Note that this function return the pointer of devfreq-event device. + */ +struct devfreq_event_dev *devfreq_event_get_edev_by_phandle(struct device *dev, + int index) +{ + struct device_node *node; + struct devfreq_event_dev *edev; + + if (!dev->of_node) { + dev_err(dev, "device does not have a device node entry\n"); + return ERR_PTR(-EINVAL); + } + + node = of_parse_phandle(dev->of_node, "devfreq-events", index); + if (!node) { + dev_err(dev, "failed to get phandle in %s node\n", + dev->of_node->full_name); + return ERR_PTR(-ENODEV); + } + + mutex_lock(&devfreq_event_list_lock); + list_for_each_entry(edev, &devfreq_event_list, node) { + if (!strcmp(edev->desc->name, node->name)) + goto out; + } + edev = NULL; +out: + mutex_unlock(&devfreq_event_list_lock); + + if (!edev) { + dev_err(dev, "unable to get devfreq-event device : %s\n", + node->name); + of_node_put(node); + return ERR_PTR(-ENODEV); + } + + of_node_put(node); + + return edev; +} +EXPORT_SYMBOL_GPL(devfreq_event_get_edev_by_phandle); + +/** + * devfreq_event_get_edev_count() - Get the count of devfreq-event dev + * @dev : the pointer to the given device + * + * Note that this function return the count of devfreq-event devices. + */ +int devfreq_event_get_edev_count(struct device *dev) +{ + int count; + + if (!dev->of_node) { + dev_err(dev, "device does not have a device node entry\n"); + return -EINVAL; + } + + count = of_property_count_elems_of_size(dev->of_node, "devfreq-events", + sizeof(u32)); + if (count < 0 ) { + dev_err(dev, + "failed to get the count of devfreq-event in %s node\n", + dev->of_node->full_name); + return count; + } + + return count; +} +EXPORT_SYMBOL_GPL(devfreq_event_get_edev_count); + +static void devfreq_event_release_edev(struct device *dev) +{ + struct devfreq_event_dev *edev = to_devfreq_event(dev); + + kfree(edev); +} + +/** + * devfreq_event_add_edev() - Add new devfreq-event device. + * @dev : the device owning the devfreq-event device being created + * @desc : the devfreq-event device's decriptor which include essential + * data for devfreq-event device. + * + * Note that this function add new devfreq-event device to devfreq-event class + * list and register the device of the devfreq-event device. + */ +struct devfreq_event_dev *devfreq_event_add_edev(struct device *dev, + struct devfreq_event_desc *desc) +{ + struct devfreq_event_dev *edev; + static atomic_t event_no = ATOMIC_INIT(0); + int ret; + + if (!dev || !desc) + return ERR_PTR(-EINVAL); + + if (!desc->name || !desc->ops) + return ERR_PTR(-EINVAL); + + if (!desc->ops->set_event || !desc->ops->get_event) + return ERR_PTR(-EINVAL); + + edev = kzalloc(sizeof(struct devfreq_event_dev), GFP_KERNEL); + if (!edev) + return ERR_PTR(-ENOMEM); + + mutex_init(&edev->lock); + edev->desc = desc; + edev->dev.parent = dev; + edev->dev.class = devfreq_event_class; + edev->dev.release = devfreq_event_release_edev; + + dev_set_name(&edev->dev, "event.%d", atomic_inc_return(&event_no) - 1); + ret = device_register(&edev->dev); + if (ret < 0) { + put_device(&edev->dev); + return ERR_PTR(ret); + } + dev_set_drvdata(&edev->dev, edev); + + INIT_LIST_HEAD(&edev->node); + + mutex_lock(&devfreq_event_list_lock); + list_add(&edev->node, &devfreq_event_list); + mutex_unlock(&devfreq_event_list_lock); + + return edev; +} +EXPORT_SYMBOL_GPL(devfreq_event_add_edev); + +/** + * devfreq_event_remove_edev() - Remove the devfreq-event device registered. + * @dev : the devfreq-event device + * + * Note that this function remove the registered devfreq-event device. + */ +int devfreq_event_remove_edev(struct devfreq_event_dev *edev) +{ + if (!edev) + return -EINVAL; + + mutex_lock(&devfreq_event_list_lock); + WARN_ON(edev->enable_count); + list_del(&edev->node); + device_unregister(&edev->dev); + mutex_unlock(&devfreq_event_list_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(devfreq_event_remove_edev); + +/* + * Device attributes for devfreq-event class. + */ +static ssize_t name_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct devfreq_event_dev *edev = to_devfreq_event(dev); + + if (!edev || !edev->desc) + return -EINVAL; + + return sprintf(buf, "%s\n", edev->desc->name); +} +static DEVICE_ATTR_RO(name); + +static ssize_t enable_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct devfreq_event_dev *edev = to_devfreq_event(dev); + + if (!edev || !edev->desc) + return -EINVAL; + + return sprintf(buf, "%d\n", edev->enable_count); +} +static DEVICE_ATTR_RO(enable_count); + +static struct attribute *devfreq_event_attrs[] = { + &dev_attr_name.attr, + &dev_attr_enable_count.attr, + NULL, +}; +ATTRIBUTE_GROUPS(devfreq_event); + +static int __init devfreq_event_init(void) +{ + devfreq_event_class = class_create(THIS_MODULE, "devfreq-event"); + if (IS_ERR(devfreq_event_class)) { + pr_err("%s: couldn't create class\n", __FILE__); + return PTR_ERR(devfreq_event_class); + } + + devfreq_event_class->dev_groups = devfreq_event_groups; + + return 0; +} +subsys_initcall(devfreq_event_init); + +static void __exit devfreq_event_exit(void) +{ + class_destroy(devfreq_event_class); +} +module_exit(devfreq_event_exit); + +MODULE_AUTHOR("Chanwoo Choi "); +MODULE_DESCRIPTION("DEVFREQ-Event class support"); +MODULE_LICENSE("GPL"); diff --git a/drivers/devfreq/event/Kconfig b/drivers/devfreq/event/Kconfig new file mode 100644 index 00000000000000..1ced42c843347a --- /dev/null +++ b/drivers/devfreq/event/Kconfig @@ -0,0 +1,16 @@ +menuconfig PM_DEVFREQ_EVENT + bool "DEVFREQ-Event device Support" + help + The devfreq-event device provide the raw data and events which + indicate the current state of devfreq-event device. The provided + data from devfreq-event device is used to monitor the state of + device and determine the suitable size of resource to reduce the + wasted resource. + + The devfreq-event device can support the various type of events + (e.g., raw data, utilization, latency, bandwidth). The events + may be used by devfreq governor and other subsystem. + +if PM_DEVFREQ_EVENT + +endif # PM_DEVFREQ_EVENT diff --git a/drivers/devfreq/event/Makefile b/drivers/devfreq/event/Makefile new file mode 100644 index 00000000000000..dc56005d85d4b5 --- /dev/null +++ b/drivers/devfreq/event/Makefile @@ -0,0 +1 @@ +# Exynos DEVFREQ Event Drivers diff --git a/include/linux/devfreq-event.h b/include/linux/devfreq-event.h new file mode 100644 index 00000000000000..b7363f518d7770 --- /dev/null +++ b/include/linux/devfreq-event.h @@ -0,0 +1,170 @@ +/* + * devfreq-event: a framework to provide raw data and events of devfreq devices + * + * Copyright (C) 2014 Samsung Electronics + * Author: Chanwoo Choi + * + * 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. + */ + +#ifndef __LINUX_DEVFREQ_EVENT_H__ +#define __LINUX_DEVFREQ_EVENT_H__ + +#include + +/** + * struct devfreq_event_dev - the devfreq-event device + * + * @node : Contain the devfreq-event device that have been registered. + * @dev : the device registered by devfreq-event class. dev.parent is + * the device using devfreq-event. + * @lock : a mutex to protect accessing devfreq-event. + * @enable_count: the number of enable function have been called. + * @desc : the description for devfreq-event device. + * + * This structure contains devfreq-event device information. + */ +struct devfreq_event_dev { + struct list_head node; + + struct device dev; + struct mutex lock; + u32 enable_count; + + const struct devfreq_event_desc *desc; +}; + +/** + * struct devfreq_event_data - the devfreq-event data + * + * @event : the load of devfreq-event device for polling period + * @total_event : the total load of devfreq-event device for polling period + * + * This structure contains the data of devfreq-event device for polling period. + */ +struct devfreq_event_data { + unsigned long event; + unsigned long total_event; +}; + +/** + * struct devfreq_event_ops - the operations of devfreq-event device + * + * @enable : Enable the devfreq-event device. + * @disable : Disable the devfreq-event device. + * @reset : Reset all setting of the devfreq-event device. + * @set_event : Set the specific event type for the devfreq-event device. + * @get_event : Get the result of the devfreq-event devie with specific + * event type. + * + * This structure contains devfreq-event device operations which can be + * implemented by devfreq-event device drivers. + */ +struct devfreq_event_ops { + /* Optional functions */ + int (*enable)(struct devfreq_event_dev *edev); + int (*disable)(struct devfreq_event_dev *edev); + int (*reset)(struct devfreq_event_dev *edev); + + /* Mandatory functions */ + int (*set_event)(struct devfreq_event_dev *edev); + int (*get_event)(struct devfreq_event_dev *edev, + struct devfreq_event_data *edata); +}; + +/** + * struct devfreq_event_desc - the descriptor of devfreq-event device + * + * @name : the name of devfreq-event device. + * @driver_data : the private data for devfreq-event driver. + * @ops : the operation to control devfreq-event device. + * + * Each devfreq-event device is described with a this structure. + * This structure contains the various data for devfreq-event device. + */ +struct devfreq_event_desc { + const char *name; + void *driver_data; + + struct devfreq_event_ops *ops; +}; + +#if defined(CONFIG_PM_DEVFREQ_EVENT) +extern int devfreq_event_enable_edev(struct devfreq_event_dev *edev); +extern int devfreq_event_disable_edev(struct devfreq_event_dev *edev); +extern bool devfreq_event_is_enabled(struct devfreq_event_dev *edev); +extern int devfreq_event_set_event(struct devfreq_event_dev *edev); +extern int devfreq_event_get_event(struct devfreq_event_dev *edev, + struct devfreq_event_data *edata); +extern int devfreq_event_reset_event(struct devfreq_event_dev *edev); +extern void *devfreq_event_get_drvdata(struct devfreq_event_dev *edev); +extern struct devfreq_event_dev *devfreq_event_get_edev_by_phandle( + struct device *dev, int index); +extern int devfreq_event_get_edev_count(struct device *dev); +extern struct devfreq_event_dev *devfreq_event_add_edev(struct device *dev, + struct devfreq_event_desc *desc); +extern int devfreq_event_remove_edev(struct devfreq_event_dev *edev); + +#else +static inline int devfreq_event_enable_edev(struct devfreq_event_dev *edev) +{ + return -EINVAL; +} + +static inline int devfreq_event_disable_edev(struct devfreq_event_dev *edev) +{ + return -EINVAL; +} + +static inline bool devfreq_event_is_enabled(struct devfreq_event_dev *edev) +{ + return false; +} + +static inline int devfreq_event_set_event(struct devfreq_event_dev *edev) +{ + return -EINVAL; +} + +static inline int devfreq_event_get_event(struct devfreq_event_dev *edev, + struct devfreq_event_data *edata) +{ + return -EINVAL; +} + +static inline int devfreq_event_reset_event(struct devfreq_event_dev *edev) +{ + return -EINVAL; +} + +static inline void *devfreq_event_get_drvdata(struct devfreq_event_dev *edev) +{ + return ERR_PTR(-EINVAL); +} + +static inline struct devfreq_event_dev *devfreq_event_get_edev_by_phandle( + struct device *dev, int index) +{ + return ERR_PTR(-EINVAL); +} + +static inline int devfreq_event_get_edev_count(struct device *dev) +{ + return -EINVAL; +} + +static inline struct devfreq_event_dev *devfreq_event_add_edev(struct device *dev, + struct devfreq_event_desc *desc) +{ + return ERR_PTR(-EINVAL); +} + +static inline int devfreq_event_remove_edev(struct devfreq_event_dev *edev) +{ + return -EINVAL; +} +#endif /* CONFIG_PM_DEVFREQ_EVENT */ + +#endif /* __LINUX_DEVFREQ_EVENT_H__ */ From 9290a6f4fb71e7f6452f211f3ca665c4a2691c90 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 12 Jan 2015 21:34:50 +0900 Subject: [PATCH 616/788] devfreq: event: Add resource-managed function for devfreq-event device This patch add the resource-managed function for devfreq-event device as following functions. The devm_devfreq_event_add_edev() manages automatically the memory of devfreq-event device using resource management. - devm_devfreq_event_add_edev() - devm_devfreq_event_remove_edev() Cc: Myungjoo Ham Cc: Kyungmin Park Signed-off-by: Chanwoo Choi --- drivers/devfreq/devfreq-event.c | 63 +++++++++++++++++++++++++++++++++ include/linux/devfreq-event.h | 16 +++++++++ 2 files changed, 79 insertions(+) diff --git a/drivers/devfreq/devfreq-event.c b/drivers/devfreq/devfreq-event.c index 9ba5ba41792e9b..57529af796ab81 100644 --- a/drivers/devfreq/devfreq-event.c +++ b/drivers/devfreq/devfreq-event.c @@ -389,6 +389,69 @@ int devfreq_event_remove_edev(struct devfreq_event_dev *edev) } EXPORT_SYMBOL_GPL(devfreq_event_remove_edev); +static int devm_devfreq_event_match(struct device *dev, void *res, void *data) +{ + struct devfreq_event_dev **r = res; + + if (WARN_ON(!r || !*r)) + return 0; + + return *r == data; +} + +static void devm_devfreq_event_release(struct device *dev, void *res) +{ + devfreq_event_remove_edev(*(struct devfreq_event_dev **)res); +} + +/** + * devm_devfreq_event_add_edev() - Resource-managed devfreq_event_add_edev() + * @dev : the device owning the devfreq-event device being created + * @desc : the devfreq-event device's decriptor which include essential + * data for devfreq-event device. + * + * Note that this function manages automatically the memory of devfreq-event + * device using device resource management and simplify the free operation + * for memory of devfreq-event device. + */ +struct devfreq_event_dev *devm_devfreq_event_add_edev(struct device *dev, + struct devfreq_event_desc *desc) +{ + struct devfreq_event_dev **ptr, *edev; + + ptr = devres_alloc(devm_devfreq_event_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + edev = devfreq_event_add_edev(dev, desc); + if (IS_ERR(edev)) { + devres_free(ptr); + return ERR_PTR(-ENOMEM); + } + + *ptr = edev; + devres_add(dev, ptr); + + return edev; +} +EXPORT_SYMBOL_GPL(devm_devfreq_event_add_edev); + +/** + * devm_devfreq_event_remove_edev()- Resource-managed devfreq_event_remove_edev() + * @dev : the device owning the devfreq-event device being created + * @edev : the devfreq-event device + * + * Note that this function manages automatically the memory of devfreq-event + * device using device resource management. + */ +void devm_devfreq_event_remove_edev(struct device *dev, + struct devfreq_event_dev *edev) +{ + WARN_ON(devres_release(dev, devm_devfreq_event_release, + devm_devfreq_event_match, edev)); +} +EXPORT_SYMBOL_GPL(devm_devfreq_event_remove_edev); + /* * Device attributes for devfreq-event class. */ diff --git a/include/linux/devfreq-event.h b/include/linux/devfreq-event.h index b7363f518d7770..b6e3afa62021a7 100644 --- a/include/linux/devfreq-event.h +++ b/include/linux/devfreq-event.h @@ -106,6 +106,10 @@ extern int devfreq_event_get_edev_count(struct device *dev); extern struct devfreq_event_dev *devfreq_event_add_edev(struct device *dev, struct devfreq_event_desc *desc); extern int devfreq_event_remove_edev(struct devfreq_event_dev *edev); +extern struct devfreq_event_dev *devm_devfreq_event_add_edev(struct device *dev, + struct devfreq_event_desc *desc); +extern void devm_devfreq_event_remove_edev(struct device *dev, + struct devfreq_event_dev *edev); #else static inline int devfreq_event_enable_edev(struct devfreq_event_dev *edev) @@ -165,6 +169,18 @@ static inline int devfreq_event_remove_edev(struct devfreq_event_dev *edev) { return -EINVAL; } + +static inline struct devfreq_event_dev *devm_devfreq_event_add_edev( + struct device *dev, + struct devfreq_event_desc *desc) +{ + return ERR_PTR(-EINVAL); +} + +static inline void devm_devfreq_event_remove_edev(struct device *dev, + struct devfreq_event_dev *edev) +{ +} #endif /* CONFIG_PM_DEVFREQ_EVENT */ #endif /* __LINUX_DEVFREQ_EVENT_H__ */ From 2f5d4b31b01bc73a432aa66e0bf27d910b4d98a6 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 12 Jan 2015 21:34:51 +0900 Subject: [PATCH 617/788] devfreq: event: Add exynos-ppmu devfreq-event driver This patch adds exynos-ppmu devfreq-event driver to get performance data of each IP for Samsung Exynos SoC. These event from Exynos PPMU provide useful information about the behavior of the SoC that you can use when analyzing system performance, and made visible and can be counted using logic in each IP. This patch is based on existing drivers/devfreq/exynos/exynos-ppmu.c Cc: MyungJoo Ham Cc: Kyungmin Park Signed-off-by: Chanwoo Choi Acked-by: MyungJoo Ham --- drivers/devfreq/event/Kconfig | 9 + drivers/devfreq/event/Makefile | 1 + drivers/devfreq/event/exynos-ppmu.c | 398 ++++++++++++++++++++++++++++ 3 files changed, 408 insertions(+) create mode 100644 drivers/devfreq/event/exynos-ppmu.c diff --git a/drivers/devfreq/event/Kconfig b/drivers/devfreq/event/Kconfig index 1ced42c843347a..a11720affc319f 100644 --- a/drivers/devfreq/event/Kconfig +++ b/drivers/devfreq/event/Kconfig @@ -13,4 +13,13 @@ menuconfig PM_DEVFREQ_EVENT if PM_DEVFREQ_EVENT +config DEVFREQ_EVENT_EXYNOS_PPMU + bool "EXYNOS PPMU (Platform Performance Monitoring Unit) DEVFREQ event Driver" + depends on ARCH_EXYNOS + select PM_OPP + help + This add the devfreq-event driver for Exynos SoC. It provides PPMU + (Platform Performance Monitoring Unit) counters to estimate the + utilization of each module. + endif # PM_DEVFREQ_EVENT diff --git a/drivers/devfreq/event/Makefile b/drivers/devfreq/event/Makefile index dc56005d85d4b5..be146ead79cfb6 100644 --- a/drivers/devfreq/event/Makefile +++ b/drivers/devfreq/event/Makefile @@ -1 +1,2 @@ # Exynos DEVFREQ Event Drivers +obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_PPMU) += exynos-ppmu.o diff --git a/drivers/devfreq/event/exynos-ppmu.c b/drivers/devfreq/event/exynos-ppmu.c new file mode 100644 index 00000000000000..4dd78c5ecfc779 --- /dev/null +++ b/drivers/devfreq/event/exynos-ppmu.c @@ -0,0 +1,398 @@ +/* + * exynos_ppmu.c - EXYNOS PPMU (Platform Performance Monitoring Unit) support + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * Author : Chanwoo Choi + * + * 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 driver is based on drivers/devfreq/exynos/exynos_ppmu.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PPMU_ENABLE BIT(0) +#define PPMU_DISABLE 0x0 +#define PPMU_CYCLE_RESET BIT(1) +#define PPMU_COUNTER_RESET BIT(2) + +#define PPMU_ENABLE_COUNT0 BIT(0) +#define PPMU_ENABLE_COUNT1 BIT(1) +#define PPMU_ENABLE_COUNT2 BIT(2) +#define PPMU_ENABLE_COUNT3 BIT(3) +#define PPMU_ENABLE_CYCLE BIT(31) + +#define PPMU_CNTENS 0x10 +#define PPMU_CNTENC 0x20 +#define PPMU_FLAG 0x50 +#define PPMU_CCNT_OVERFLOW BIT(31) +#define PPMU_CCNT 0x100 + +#define PPMU_PMCNT0 0x110 +#define PPMU_PMCNT_OFFSET 0x10 +#define PMCNT_OFFSET(x) (PPMU_PMCNT0 + (PPMU_PMCNT_OFFSET * x)) + +#define PPMU_BEVT0SEL 0x1000 +#define PPMU_BEVTSEL_OFFSET 0x100 +#define PPMU_BEVTSEL(x) (PPMU_BEVT0SEL + (x * PPMU_BEVTSEL_OFFSET)) + +#define RD_DATA_COUNT 0x5 +#define WR_DATA_COUNT 0x6 +#define RDWR_DATA_COUNT 0x7 + +enum ppmu_counter { + PPMU_PMNCNT0 = 0, + PPMU_PMNCNT1, + PPMU_PMNCNT2, + PPMU_PMNCNT3, + + PPMU_PMNCNT_MAX, +}; + +struct exynos_ppmu_data { + struct devfreq_event_dev **edev; + struct devfreq_event_desc *desc; + unsigned int num_events; + + struct device *dev; + struct clk *clk_ppmu; + struct mutex lock; + + struct __exynos_ppmu { + void __iomem *base; + unsigned int event[PPMU_PMNCNT_MAX]; + unsigned int count[PPMU_PMNCNT_MAX]; + bool ccnt_overflow; + bool count_overflow[PPMU_PMNCNT_MAX]; + } ppmu; +}; + +#define PPMU_EVENT(name) \ + { "ppmu-event0-"#name, PPMU_PMNCNT0 }, \ + { "ppmu-event1-"#name, PPMU_PMNCNT1 }, \ + { "ppmu-event2-"#name, PPMU_PMNCNT2 }, \ + { "ppmu-event3-"#name, PPMU_PMNCNT3 } + +struct __exynos_ppmu_events { + char *name; + int id; +} ppmu_events[] = { + /* For Exynos3250, Exynos4 and Exynos5260 */ + PPMU_EVENT(g3d), + PPMU_EVENT(fsys), + + /* For Exynos4 SoCs and Exynos3250 */ + PPMU_EVENT(dmc0), + PPMU_EVENT(dmc1), + PPMU_EVENT(cpu), + PPMU_EVENT(rightbus), + PPMU_EVENT(leftbus), + PPMU_EVENT(lcd0), + PPMU_EVENT(camif), + + /* Only for Exynos3250 and Exynos5260 */ + PPMU_EVENT(mfc), + + /* Only for Exynos4 SoCs */ + PPMU_EVENT(mfc-left), + PPMU_EVENT(mfc-right), + + /* Only for Exynos5260 SoCs */ + PPMU_EVENT(drex0-s0), + PPMU_EVENT(drex0-s1), + PPMU_EVENT(drex1-s0), + PPMU_EVENT(drex1-s1), + PPMU_EVENT(eagle), + PPMU_EVENT(kfc), + PPMU_EVENT(isp), + PPMU_EVENT(fimc), + PPMU_EVENT(gscl), + PPMU_EVENT(mscl), + PPMU_EVENT(fimd0x), + PPMU_EVENT(fimd1x), + { /* sentinel */ }, +}; + +static int exynos_ppmu_find_ppmu_id(struct devfreq_event_dev *edev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ppmu_events); i++) + if (!strcmp(edev->desc->name, ppmu_events[i].name)) + return ppmu_events[i].id; + + return -EINVAL; +} + +static int exynos_ppmu_reset(struct devfreq_event_dev *edev) +{ + struct exynos_ppmu_data *data = devfreq_event_get_drvdata(edev); + + __raw_writel(PPMU_CYCLE_RESET | PPMU_COUNTER_RESET, data->ppmu.base); + __raw_writel(PPMU_ENABLE_CYCLE | + PPMU_ENABLE_COUNT0 | + PPMU_ENABLE_COUNT1 | + PPMU_ENABLE_COUNT2 | + PPMU_ENABLE_COUNT3, + data->ppmu.base + PPMU_CNTENS); + + return 0; +} + +static int exynos_ppmu_disable(struct devfreq_event_dev *edev) +{ + struct exynos_ppmu_data *data = devfreq_event_get_drvdata(edev); + + /* Reset the performance and cycle counters */ + exynos_ppmu_reset(edev); + + /* Disable event of PPMU */ + __raw_writel(PPMU_ENABLE_CYCLE | + PPMU_ENABLE_COUNT0 | + PPMU_ENABLE_COUNT1 | + PPMU_ENABLE_COUNT2 | + PPMU_ENABLE_COUNT3, + data->ppmu.base + PPMU_CNTENC); + + /* Stop monitoring of PPMU IP */ + __raw_writel(PPMU_DISABLE, data->ppmu.base); + + return 0; +} + +static int exynos_ppmu_set_event(struct devfreq_event_dev *edev) +{ + struct exynos_ppmu_data *data = devfreq_event_get_drvdata(edev); + int id = exynos_ppmu_find_ppmu_id(edev); + + if (id < 0) + return id; + + /* Reset the performance and cycle counters */ + exynos_ppmu_reset(edev); + + /* Setup count registers to monitor read/write transactions */ + __raw_writel(RDWR_DATA_COUNT, data->ppmu.base + PPMU_BEVTSEL(id)); + + /* Start monitoring of PPMU IP */ + __raw_writel(PPMU_ENABLE, data->ppmu.base); + + return 0; +} + +static int exynos_ppmu_get_event(struct devfreq_event_dev *edev, + struct devfreq_event_data *edata) +{ + struct exynos_ppmu_data *data = devfreq_event_get_drvdata(edev); + int id = exynos_ppmu_find_ppmu_id(edev); + + if (id < 0) + return -EINVAL; + + /* Stop monitoring of PPMU IP */ + __raw_writel(PPMU_DISABLE, data->ppmu.base); + + /* Read total count cycle from of PPMU IP */ + edata->total_event = __raw_readl(data->ppmu.base + PPMU_CCNT); + + if (id == PPMU_PMNCNT3) + edata->event = + ((__raw_readl(data->ppmu.base + PMCNT_OFFSET(id)) << 8) + | __raw_readl(data->ppmu.base + PMCNT_OFFSET(id + 1))); + else + edata->event = __raw_readl(data->ppmu.base + PMCNT_OFFSET(id)); + + dev_dbg(&edev->dev, "%s (event: %ld/%ld)\n", edev->desc->name, + edata->event, edata->total_event); + + return 0; +} + +static struct devfreq_event_ops exynos_ppmu_ops = { + .disable = exynos_ppmu_disable, + .set_event = exynos_ppmu_set_event, + .get_event = exynos_ppmu_get_event, +}; + +static int of_get_devfreq_events(struct device_node *np, + struct exynos_ppmu_data *data) +{ + struct devfreq_event_desc *desc; + struct device *dev = data->dev; + struct device_node *events_np, *node; + int i, j, count; + + events_np = of_get_child_by_name(np, "events"); + if (!events_np) { + dev_err(dev, + "failed to get child node of devfreq-event devices\n"); + return -EINVAL; + } + + count = of_get_child_count(events_np); + desc = devm_kzalloc(dev, sizeof(*desc) * count, GFP_KERNEL); + if (!desc) + return -ENOMEM; + data->num_events = count; + + j = 0; + for_each_child_of_node(events_np, node) { + for (i = 0; i < ARRAY_SIZE(ppmu_events); i++) { + if (!ppmu_events[i].name) + continue; + + if (!of_node_cmp(node->name, ppmu_events[i].name)) + break; + } + + if (i == ARRAY_SIZE(ppmu_events)) { + dev_warn(dev, + "don't know how to configure events : %s\n", + node->name); + continue; + } + + desc[j].ops = &exynos_ppmu_ops; + desc[j].driver_data = data; + + of_property_read_string(node, "event-name", &desc[j].name); + + j++; + + of_node_put(node); + } + data->desc = desc; + + of_node_put(events_np); + + return 0; +} + +static int exynos_ppmu_parse_dt(struct exynos_ppmu_data *data) +{ + struct device *dev = data->dev; + struct device_node *np = dev->of_node; + int ret = 0; + + if (!np) { + dev_err(dev, "failed to find devicetree node\n"); + return -EINVAL; + } + + /* Maps the memory mapped IO to control PPMU register */ + data->ppmu.base = of_iomap(np, 0); + if (IS_ERR_OR_NULL(data->ppmu.base)) { + dev_err(dev, "failed to map memory region\n"); + return -ENOMEM; + } + + data->clk_ppmu = devm_clk_get(dev, "ppmu"); + if (IS_ERR(data->clk_ppmu)) { + data->clk_ppmu = NULL; + dev_warn(dev, "cannot get PPMU clock\n"); + } + + ret = of_get_devfreq_events(np, data); + if (ret < 0) { + dev_err(dev, "failed to parse exynos ppmu dt node\n"); + goto err; + } + + return 0; + +err: + iounmap(data->ppmu.base); + + return ret; +} + +static int exynos_ppmu_probe(struct platform_device *pdev) +{ + struct exynos_ppmu_data *data; + struct devfreq_event_dev **edev; + struct devfreq_event_desc *desc; + int i, ret = 0, size; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + mutex_init(&data->lock); + data->dev = &pdev->dev; + + /* Parse dt data to get resource */ + ret = exynos_ppmu_parse_dt(data); + if (ret < 0) { + dev_err(&pdev->dev, + "failed to parse devicetree for resource\n"); + return ret; + } + desc = data->desc; + + size = sizeof(struct devfreq_event_dev *) * data->num_events; + data->edev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!data->edev) { + dev_err(&pdev->dev, + "failed to allocate memory devfreq-event devices\n"); + return -ENOMEM; + } + edev = data->edev; + platform_set_drvdata(pdev, data); + + for (i = 0; i < data->num_events; i++) { + edev[i] = devm_devfreq_event_add_edev(&pdev->dev, &desc[i]); + if (IS_ERR(edev)) { + ret = PTR_ERR(edev); + dev_err(&pdev->dev, + "failed to add devfreq-event device\n"); + goto err; + } + } + + clk_prepare_enable(data->clk_ppmu); + + return 0; +err: + iounmap(data->ppmu.base); + + return ret; +} + +static int exynos_ppmu_remove(struct platform_device *pdev) +{ + struct exynos_ppmu_data *data = platform_get_drvdata(pdev); + + clk_disable_unprepare(data->clk_ppmu); + iounmap(data->ppmu.base); + + return 0; +} + +static struct of_device_id exynos_ppmu_id_match[] = { + { .compatible = "samsung,exynos-ppmu", }, + { /* sentinel */ }, +}; + +static struct platform_driver exynos_ppmu_driver = { + .probe = exynos_ppmu_probe, + .remove = exynos_ppmu_remove, + .driver = { + .name = "exynos-ppmu", + .of_match_table = exynos_ppmu_id_match, + }, +}; +module_platform_driver(exynos_ppmu_driver); + +MODULE_DESCRIPTION("Exynos PPMU(Platform Performance Monitoring Unit) driver"); +MODULE_AUTHOR("Chanwoo Choi "); +MODULE_LICENSE("GPL"); From c75a61eabae4d31c0d453a29ea3bb757b2d30fe9 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 12 Jan 2015 21:34:52 +0900 Subject: [PATCH 618/788] devfreq: event: Add documentation for exynos-ppmu devfreq-event driver This patch adds the documentation for Exynos PPMU (Platform Performance Monitoring Unit) devfreq-event driver. Cc: MyungJoo Ham Cc: Kyungmin Park Signed-off-by: Chanwoo Choi Acked-by: MyungJoo Ham --- .../bindings/devfreq/event/exynos-ppmu.txt | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 Documentation/devicetree/bindings/devfreq/event/exynos-ppmu.txt diff --git a/Documentation/devicetree/bindings/devfreq/event/exynos-ppmu.txt b/Documentation/devicetree/bindings/devfreq/event/exynos-ppmu.txt new file mode 100644 index 00000000000000..b54bf3a2ff5720 --- /dev/null +++ b/Documentation/devicetree/bindings/devfreq/event/exynos-ppmu.txt @@ -0,0 +1,110 @@ + +* Samsung Exynos PPMU (Platform Performance Monitoring Unit) device + +The Samsung Exynos SoC has PPMU (Platform Performance Monitoring Unit) for +each IP. PPMU provides the primitive values to get performance data. These +PPMU events provide information of the SoC's behaviors so that you may +use to analyze system performance, to make behaviors visible and to count +usages of each IP (DMC, CPU, RIGHTBUS, LEFTBUS, CAM interface, LCD, G3D, MFC). +The Exynos PPMU driver uses the devfreq-event class to provide event data +to various devfreq devices. The devfreq devices would use the event data when +derterming the current state of each IP. + +Required properties: +- compatible: Should be "samsung,exynos-ppmu". +- reg: physical base address of each PPMU and length of memory mapped region. + +Optional properties: +- clock-names : the name of clock used by the PPMU, "ppmu" +- clocks : phandles for clock specified in "clock-names" property +- #clock-cells: should be 1. + +Example1 : PPMU nodes in exynos3250.dtsi are listed below. + + ppmu_dmc0: ppmu_dmc0@106a0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x106a0000 0x2000>; + status = "disabled"; + }; + + ppmu_dmc1: ppmu_dmc1@106b0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x106b0000 0x2000>; + status = "disabled"; + }; + + ppmu_cpu: ppmu_cpu@106c0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x106c0000 0x2000>; + status = "disabled"; + }; + + ppmu_rightbus: ppmu_rightbus@112a0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x112a0000 0x2000>; + clocks = <&cmu CLK_PPMURIGHT>; + clock-names = "ppmu"; + status = "disabled"; + }; + + ppmu_leftbus: ppmu_leftbus0@116a0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x116a0000 0x2000>; + clocks = <&cmu CLK_PPMULEFT>; + clock-names = "ppmu"; + status = "disabled"; + }; + +Example2 : Events of each PPMU node in exynos3250-rinato.dts are listed below. + + &ppmu_dmc0 { + status = "okay"; + + events { + ppmu_dmc0_3: ppmu-event3-dmc0 { + event-name = "ppmu-event3-dmc0"; + }; + + ppmu_dmc0_2: ppmu-event2-dmc0 { + event-name = "ppmu-event2-dmc0"; + }; + + ppmu_dmc0_1: ppmu-event1-dmc0 { + event-name = "ppmu-event1-dmc0"; + }; + + ppmu_dmc0_0: ppmu-event0-dmc0 { + event-name = "ppmu-event0-dmc0"; + }; + }; + }; + + &ppmu_dmc1 { + status = "okay"; + + events { + ppmu_dmc1_3: ppmu-event3-dmc1 { + event-name = "ppmu-event3-dmc1"; + }; + }; + }; + + &ppmu_leftbus { + status = "okay"; + + events { + ppmu_leftbus_3: ppmu-event3-leftbus { + event-name = "ppmu-event3-leftbus"; + }; + }; + }; + + &ppmu_rightbus { + status = "okay"; + + events { + ppmu_rightbus_3: ppmu-event3-rightbus { + event-name = "ppmu-event3-rightbus"; + }; + }; + }; From b4fdafa93cef3722d24f734de4e0504f83492b96 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 12 Jan 2015 21:34:53 +0900 Subject: [PATCH 619/788] ARM: dts: Add PPMU dt node for Exynos3250 SoC This patch add PPMU (Platform Performance Monitoring Unit) dt node to estimate the utilization of each IP in Exynos SoC throught DEVFREQ Event subsystem. This patch adds following PPMU dt nodes: - PPMU_DMC0 0x106a0000 - PPMU_DMC1 0x106b0000 - PPMU_RIGHTBUS 0x112A0000 - PPMU_LEFTBUS 0x116A0000 - PPMU_CAMIF 0x11AC0000 - PPMU_LCD0 0x11E40000 - PPMU_FSYS 0x12630000 - PPMU_3D 0x13220000 - PPMU_MFC 0x13660000 - PPMU_CPU 0x106c0000 Cc: Kukjin Kim Signed-off-by: Chanwoo Choi Acked-by: Kyungmin Park Acked-by: MyungJoo Ham --- arch/arm/boot/dts/exynos3250.dtsi | 74 +++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/arch/arm/boot/dts/exynos3250.dtsi b/arch/arm/boot/dts/exynos3250.dtsi index 60307f6a82b7d9..d7df2fb07bcb91 100644 --- a/arch/arm/boot/dts/exynos3250.dtsi +++ b/arch/arm/boot/dts/exynos3250.dtsi @@ -520,6 +520,80 @@ compatible = "arm,cortex-a7-pmu"; interrupts = <0 18 0>, <0 19 0>; }; + + ppmu_dmc0: ppmu_dmc0@106a0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x106a0000 0x2000>; + status = "disabled"; + }; + + ppmu_dmc1: ppmu_dmc1@106b0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x106b0000 0x2000>; + status = "disabled"; + }; + + ppmu_cpu: ppmu_cpu@106c0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x106c0000 0x2000>; + status = "disabled"; + }; + + ppmu_rightbus: ppmu_rightbus@112a0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x112a0000 0x2000>; + clocks = <&cmu CLK_PPMURIGHT>; + clock-names = "ppmu"; + status = "disabled"; + }; + + ppmu_leftbus: ppmu_leftbus0@116a0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x116a0000 0x2000>; + clocks = <&cmu CLK_PPMULEFT>; + clock-names = "ppmu"; + status = "disabled"; + }; + + ppmu_camif: ppmu_camif@11ac0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x11ac0000 0x2000>; + clocks = <&cmu CLK_PPMUCAMIF>; + clock-names = "ppmu"; + status = "disabled"; + }; + + ppmu_lcd0: ppmu_lcd0@11e40000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x11e40000 0x2000>; + clocks = <&cmu CLK_PPMULCD0>; + clock-names = "ppmu"; + status = "disabled"; + }; + + ppmu_fsys: ppmu_fsys@12630000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x12630000 0x2000>; + clocks = <&cmu CLK_PPMUFILE>; + clock-names = "ppmu"; + status = "disabled"; + }; + + ppmu_g3d: ppmu_g3d@13220000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x13220000 0x2000>; + clocks = <&cmu CLK_PPMUG3D>; + clock-names = "ppmu"; + status = "disabled"; + }; + + ppmu_mfc: ppmu_mfc@13660000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x13660000 0x2000>; + clocks = <&cmu CLK_PPMUMFC_L>; + clock-names = "ppmu"; + status = "disabled"; + }; }; }; From f2425e97e3b24df72748cb8a54e8c37dddea9a54 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 12 Jan 2015 21:34:54 +0900 Subject: [PATCH 620/788] ARM: dts: Add PPMU dt node for Exynos4 SoCs This patch add PPMU (Platform Performance Monitoring Unit) dt node for Exynos4 (Exynos4210/4212/4412) SoC. PPMU dt node is used to monitor the utilization of each IP. The Exynos4210/Exynos4212/Exynos4412 SoC includes following PPMUs: - PPMU_DMC0 0x106A_0000 - PPMU_DMC1 0x106B_0000 - PPMU_CPU 0x106C_0000 - PPMU_ACP 0x10AE_0000 - PPMU_RIGHT_BUS 0x112A_0000 - PPMU_LEFT_BUS 0x116A_0000 - PPMU_FSYS 0x1263_0000 - PPMU_LCD0 0x11E4_0000 - PPMU_CAMIF 0x11AC_0000 - PPMU_IMAGE 0x12AA_0000 - PPMU_TV 0x12E4_0000 - PPMU_3D 0x1322_0000 - PPMU_MFC_LEFT 0x1366_0000 - PPMU_MFC_RIGHT 0x1367_0000 Additionally, the Exynos4210 SoC includes following PPMUs: - PPMU_LCD1 0x1224_0000 Cc: Kukjin Kim Signed-off-by: Chanwoo Choi Acked-by: MyungJoo Ham --- arch/arm/boot/dts/exynos4.dtsi | 108 ++++++++++++++++++++++++++++++ arch/arm/boot/dts/exynos4210.dtsi | 8 +++ 2 files changed, 116 insertions(+) diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi index 04641262912d38..adc3d54105f619 100644 --- a/arch/arm/boot/dts/exynos4.dtsi +++ b/arch/arm/boot/dts/exynos4.dtsi @@ -710,4 +710,112 @@ power-domains = <&pd_tv>; status = "disabled"; }; + + ppmu_dmc0: ppmu_dmc0@106a0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x106a0000 0x2000>; + clocks = <&clock CLK_PPMUDMC0>; + clock-names = "ppmu"; + status = "disabled"; + }; + + ppmu_dmc1: ppmu_dmc1@106b0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x106b0000 0x2000>; + clocks = <&clock CLK_PPMUDMC1>; + clock-names = "ppmu"; + status = "disabled"; + }; + + ppmu_cpu: ppmu_cpu@106c0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x106c0000 0x2000>; + clocks = <&clock CLK_PPMUCPU>; + clock-names = "ppmu"; + status = "disabled"; + }; + + ppmu_acp: ppmu_acp@10ae0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x106e0000 0x2000>; + status = "disabled"; + }; + + ppmu_rightbus: ppmu_rightbus@112a0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x112a0000 0x2000>; + clocks = <&clock CLK_PPMURIGHT>; + clock-names = "ppmu"; + status = "disabled"; + }; + + ppmu_leftbus: ppmu_leftbus0@116a0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x116a0000 0x2000>; + clocks = <&clock CLK_PPMULEFT>; + clock-names = "ppmu"; + status = "disabled"; + }; + + ppmu_camif: ppmu_camif@11ac0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x11ac0000 0x2000>; + clocks = <&clock CLK_PPMUCAMIF>; + clock-names = "ppmu"; + status = "disabled"; + }; + + ppmu_lcd0: ppmu_lcd0@11e40000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x11e40000 0x2000>; + clocks = <&clock CLK_PPMULCD0>; + clock-names = "ppmu"; + status = "disabled"; + }; + + ppmu_fsys: ppmu_g3d@12630000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x12630000 0x2000>; + status = "disabled"; + }; + + ppmu_image: ppmu_image@12aa0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x12aa0000 0x2000>; + clocks = <&clock CLK_PPMUIMAGE>; + clock-names = "ppmu"; + status = "disabled"; + }; + + ppmu_tv: ppmu_tv@12e40000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x12e40000 0x2000>; + clocks = <&clock CLK_PPMUTV>; + clock-names = "ppmu"; + status = "disabled"; + }; + + ppmu_g3d: ppmu_g3d@13220000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x13220000 0x2000>; + clocks = <&clock CLK_PPMUG3D>; + clock-names = "ppmu"; + status = "disabled"; + }; + + ppmu_mfc_left: ppmu_mfc_left@13660000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x13660000 0x2000>; + clocks = <&clock CLK_PPMUMFC_L>; + clock-names = "ppmu"; + status = "disabled"; + }; + + ppmu_mfc_right: ppmu_mfc_right@13670000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x13670000 0x2000>; + clocks = <&clock CLK_PPMUMFC_R>; + clock-names = "ppmu"; + status = "disabled"; + }; }; diff --git a/arch/arm/boot/dts/exynos4210.dtsi b/arch/arm/boot/dts/exynos4210.dtsi index f551fb55677ac0..4890660c3d3ec9 100644 --- a/arch/arm/boot/dts/exynos4210.dtsi +++ b/arch/arm/boot/dts/exynos4210.dtsi @@ -210,4 +210,12 @@ <&clock CLK_SCLK_HDMI>, <&clock CLK_VP>, <&clock CLK_MOUT_MIXER>, <&clock CLK_SCLK_MIXER>; }; + + ppmu_lcd1: ppmu_lcd1@12240000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x12240000 0x2000>; + clocks = <&clock CLK_PPMULCD1>; + clock-names = "ppmu"; + status = "disabled"; + }; }; From 622ff8e9a8470b1f8d480d25a2045734ae732ab9 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 12 Jan 2015 21:34:55 +0900 Subject: [PATCH 621/788] ARM: dts: Add PPMU dt node for Exynos5260 SoC This patch adds PPMU (Performance Profiling Monitoring Unit) dt node Exynos5260 SoC. Exynos5260 SoC has following PPMU IPs: - PPMU_DREX0_S0 0x10c60000 - PPMU_DREX0_S1 0x10c70000 - PPMU_DREX1_S0 0x10c80000 - PPMU_DREX1_S1 0x10c90000 - PPMU_EAGLE 0x10cc0000 - PPMU_KFC 0x10cd0000 - PPMU_MFC 0x11040000 - PPMU_G3D 0x11880000 - PPMU_FSYS 0x12220000 - PPMU_ISP 0x13370000 - PPMU_FICM 0x13cb0000 - PPMU_GSCL 0x13e60000 - PPMU_MSCL 0x13ee0000 - PPMU_FIMD0X 0x145b0000 - PPMU_FIMD1X 0x145c0000 The drivers/devfreq/exynos/exynos5_bus.c supports the memory bus frequency/ voltage scaling of Exynos5260 SoC with DEVFREQ framework. Cc: Kukjin Kim Cc: Abhilash Kesavan Cc: Jonghwan Choi Signed-off-by: Chanwoo Choi --- arch/arm/boot/dts/exynos5260.dtsi | 90 +++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/arch/arm/boot/dts/exynos5260.dtsi b/arch/arm/boot/dts/exynos5260.dtsi index 36da38e29000d0..26f3074d24922e 100644 --- a/arch/arm/boot/dts/exynos5260.dtsi +++ b/arch/arm/boot/dts/exynos5260.dtsi @@ -307,6 +307,96 @@ fifo-depth = <64>; status = "disabled"; }; + + ppmu_drex0_s0: ppmu_drex0_s0@10c60000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x10c60000 0x2000>; + status = "disabled"; + }; + + ppmu_drex0_s1: ppmu_drex0_s1@10c70000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x10c70000 0x2000>; + status = "disabled"; + }; + + ppmu_drex1_s0: ppmu_drex1_s0@10c80000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x10c80000 0x2000>; + status = "disabled"; + }; + + ppmu_drex1_s1: ppmu_drex1_s1@10c90000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x10c90000 0x2000>; + status = "disabled"; + }; + + ppmu_eagle: ppmu_eagle@10cc0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x10cc0000 0x2000>; + status = "disabled"; + }; + + ppmu_kfc: ppmu_kfc@10cd0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x10cd0000 0x2000>; + status = "disabled"; + }; + + ppmu_mfc: ppmu_mfc@11040000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x11040000 0x2000>; + status = "disabled"; + }; + + ppmu_g3d: ppmu_g3d@11880000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x11880000 0x2000>; + status = "disabled"; + }; + + ppmu_fsys: ppmu_fsys@12220000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x12220000 0x2000>; + status = "disabled"; + }; + + ppmu_isp: ppmu_isp@13370000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x13370000 0x2000>; + status = "disabled"; + }; + + ppmu_fimc: ppmu_fimc@13cb0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x13cb0000 0x2000>; + status = "disabled"; + }; + + ppmu_gscl: ppmu_gscl@13e60000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x13e60000 0x2000>; + status = "disabled"; + }; + + ppmu_mscl: ppmu_gscl@13ee0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x13ee0000 0x2000>; + status = "disabled"; + }; + + ppmu_fimd0x: ppmu_fimd0x@145b0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x145b0000 0x2000>; + status = "disabled"; + }; + + ppmu_fimd1x: ppmu_fimd1x@145c0000 { + compatible = "samsung,exynos-ppmu"; + reg = <0x145c0000 0x2000>; + status = "disabled"; + }; }; }; From 1fbc482aaf48c166554c7515ea9d05e58141573d Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 12 Jan 2015 21:34:56 +0900 Subject: [PATCH 622/788] ARM: dts: exynos: Add PPMU node to Exynos3250-based Rinato/Monk board This patch add PPMU dt node to Exynos3250-base Rinato/Monk board. The PPMU node is used to get the utilization of DMC0/DMC1/LEFTBUS/RIGHTBUS Block. Cc: Kukjin Kim Signed-off-by: Chanwoo Choi Acked-by: Kyungmin Park --- arch/arm/boot/dts/exynos3250-monk.dts | 40 +++++++++++++++++++++++++ arch/arm/boot/dts/exynos3250-rinato.dts | 40 +++++++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/arch/arm/boot/dts/exynos3250-monk.dts b/arch/arm/boot/dts/exynos3250-monk.dts index 24822aa98057a0..fcceb59a7bef84 100644 --- a/arch/arm/boot/dts/exynos3250-monk.dts +++ b/arch/arm/boot/dts/exynos3250-monk.dts @@ -420,6 +420,46 @@ status = "okay"; }; +&ppmu_dmc0 { + status = "okay"; + + events { + ppmu_dmc0_3: ppmu-event3-dmc0 { + event-name = "ppmu-event3-dmc0"; + }; + }; +}; + +&ppmu_dmc1 { + status = "okay"; + + events { + ppmu_dmc1_3: ppmu-event3-dmc1 { + event-name = "ppmu-event3-dmc1"; + }; + }; +}; + +&ppmu_leftbus { + status = "okay"; + + events { + ppmu_leftbus_3: ppmu-event3-leftbus { + event-name = "ppmu-event3-leftbus"; + }; + }; +}; + +&ppmu_rightbus { + status = "okay"; + + events { + ppmu_rightbus_3: ppmu-event3-rightbus { + event-name = "ppmu-event3-rightbus"; + }; + }; +}; + &xusbxti { clock-frequency = <24000000>; }; diff --git a/arch/arm/boot/dts/exynos3250-rinato.dts b/arch/arm/boot/dts/exynos3250-rinato.dts index 80aa8b4c4a3d60..bb9865ed06851c 100644 --- a/arch/arm/boot/dts/exynos3250-rinato.dts +++ b/arch/arm/boot/dts/exynos3250-rinato.dts @@ -523,6 +523,46 @@ status = "okay"; }; +&ppmu_dmc0 { + status = "okay"; + + events { + ppmu_dmc0_3: ppmu-event3-dmc0 { + event-name = "ppmu-event3-dmc0"; + }; + }; +}; + +&ppmu_dmc1 { + status = "okay"; + + events { + ppmu_dmc1_3: ppmu-event3-dmc1 { + event-name = "ppmu-event3-dmc1"; + }; + }; +}; + +&ppmu_leftbus { + status = "okay"; + + events { + ppmu_leftbus_3: ppmu-event3-leftbus { + event-name = "ppmu-event3-leftbus"; + }; + }; +}; + +&ppmu_rightbus { + status = "okay"; + + events { + ppmu_rightbus_3: ppmu-event3-rightbus { + event-name = "ppmu-event3-rightbus"; + }; + }; +}; + &xusbxti { clock-frequency = <24000000>; }; From 20298292164e1765ebd916a8582f65bdbfc959ef Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 12 Jan 2015 21:34:57 +0900 Subject: [PATCH 623/788] ARM: dts: exynos: Add PPMU node for Exynos4412-based TRATS2 board This patch add dt node for PPMU_{DMC0|DMC1|LEFTBUS|RIGHTBUS} for exynos4412-trats2 board. Each PPMU dt node includes one event of 'PPMU Count3'. Cc: Kukjin Kim Cc: Myungjoo Ham Cc: Kyungmin Park Signed-off-by: Chanwoo Choi Acked-by: MyungJoo Ham --- arch/arm/boot/dts/exynos4412-trats2.dts | 40 +++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/arch/arm/boot/dts/exynos4412-trats2.dts b/arch/arm/boot/dts/exynos4412-trats2.dts index 29231b4526433e..70eac857c985c2 100644 --- a/arch/arm/boot/dts/exynos4412-trats2.dts +++ b/arch/arm/boot/dts/exynos4412-trats2.dts @@ -865,6 +865,46 @@ }; }; +&ppmu_dmc0 { + status = "okay"; + + events { + ppmu_dmc0_3: ppmu-event3-dmc0 { + event-name = "ppmu-event3-dmc0"; + }; + }; +}; + +&ppmu_dmc1 { + status = "okay"; + + events { + ppmu_dmc1_3: ppmu-event3-dmc1 { + event-name = "ppmu-event3-dmc1"; + }; + }; +}; + +&ppmu_leftbus { + status = "okay"; + + events { + ppmu_leftbus_3: ppmu-event3-leftbus { + event-name = "ppmu-event3-leftbus"; + }; + }; +}; + +&ppmu_rightbus { + status = "okay"; + + events { + ppmu_rightbus_3: ppmu-event3-rightbus { + event-name = "ppmu-event3-rightbus"; + }; + }; +}; + &pinctrl_0 { pinctrl-names = "default"; pinctrl-0 = <&sleep0>; From 5dcfae09304251711c9092a0fbff9693fb57c151 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sun, 15 Feb 2015 00:14:02 +0100 Subject: [PATCH 624/788] ARM: dts: odroid: Add PPMU node --- .../boot/dts/exynos4412-odroid-common.dtsi | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index 456d26a6164a8e..8d41f9e7f22a7f 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -447,6 +447,46 @@ }; }; +&ppmu_dmc0 { + status = "okay"; + + events { + ppmu_dmc0_3: ppmu-event3-dmc0 { + event-name = "ppmu-event3-dmc0"; + }; + }; +}; + +&ppmu_dmc1 { + status = "okay"; + + events { + ppmu_dmc1_3: ppmu-event3-dmc1 { + event-name = "ppmu-event3-dmc1"; + }; + }; +}; + +&ppmu_leftbus { + status = "okay"; + + events { + ppmu_leftbus_3: ppmu-event3-leftbus { + event-name = "ppmu-event3-leftbus"; + }; + }; +}; + +&ppmu_rightbus { + status = "okay"; + + events { + ppmu_rightbus_3: ppmu-event3-rightbus { + event-name = "ppmu-event3-rightbus"; + }; + }; +}; + &pinctrl_1 { gpio_power_key: power_key { samsung,pins = "gpx1-3"; From cf3352c4cc31486812e9a9df4fefcded8250d01d Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Thu, 15 Jan 2015 13:33:31 +0100 Subject: [PATCH 625/788] patchset: [PATCHv3 0/8] devfreq: Add generic exynos memory-bus frequency driver This is the old series, before it was renamed to: "devfreq: Add devfreq-event class to provide raw data for devfreq device" --- PATCHES | 1 + 1 file changed, 1 insertion(+) diff --git a/PATCHES b/PATCHES index 12f39a5d04ba24..b0d680f92c3083 100644 --- a/PATCHES +++ b/PATCHES @@ -1,3 +1,4 @@ [PATCH v12 0/9] Enable L2 cache support on Exynos4210/4x12 SoCs [PATCH v2 0/6] Enable HDMI support on Exynos platforms [PATCH V3 00/15] ASoC: samsung: Add clk provider for I2S internal clocks +[PATCHv3 0/8] devfreq: Add generic exynos memory-bus frequency driver From 1446667e1bd283b24949dc7e3bba2156cbbd23f2 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 23 Jan 2015 13:09:54 +0100 Subject: [PATCH 626/788] thermal: exynos: cosmetic: Correct comment format Signed-off-by: Lukasz Majewski --- drivers/thermal/samsung/exynos_tmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c index d2f1e62a423280..5000727c8c2b87 100644 --- a/drivers/thermal/samsung/exynos_tmu.c +++ b/drivers/thermal/samsung/exynos_tmu.c @@ -576,7 +576,7 @@ static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp) #define exynos5440_tmu_set_emulation NULL static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp) { return -EINVAL; } -#endif/*CONFIG_THERMAL_EMULATION*/ +#endif /* CONFIG_THERMAL_EMULATION */ static int exynos4210_tmu_read(struct exynos_tmu_data *data) { From cb514c4c3ec2962101a4899d4f1c555c14e226bf Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 23 Jan 2015 13:09:55 +0100 Subject: [PATCH 627/788] thermal: exynos: Provide thermal_exynos.h file to be included in device tree files This patch is a preparatory patch to be able to read Exynos thermal configuration from the device tree. It turned out that DTC is not able to interpret enums properly and hence it is necessary to #define those values explicitly. For this reason the ./include/dt-bindings/thermal/thermal_exynos.h file has been introduced. Signed-off-by: Lukasz Majewski --- drivers/thermal/samsung/exynos_tmu.h | 12 +++------ include/dt-bindings/thermal/thermal_exynos.h | 28 ++++++++++++++++++++ 2 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 include/dt-bindings/thermal/thermal_exynos.h diff --git a/drivers/thermal/samsung/exynos_tmu.h b/drivers/thermal/samsung/exynos_tmu.h index da3009bff6c439..7f880d2e53f56a 100644 --- a/drivers/thermal/samsung/exynos_tmu.h +++ b/drivers/thermal/samsung/exynos_tmu.h @@ -26,14 +26,6 @@ #include "exynos_thermal_common.h" -enum calibration_type { - TYPE_ONE_POINT_TRIMMING, - TYPE_ONE_POINT_TRIMMING_25, - TYPE_ONE_POINT_TRIMMING_85, - TYPE_TWO_POINT_TRIMMING, - TYPE_NONE, -}; - enum soc_type { SOC_ARCH_EXYNOS3250 = 1, SOC_ARCH_EXYNOS4210, @@ -44,6 +36,7 @@ enum soc_type { SOC_ARCH_EXYNOS5420_TRIMINFO, SOC_ARCH_EXYNOS5440, }; +#include /** * struct exynos_tmu_platform_data @@ -115,8 +108,9 @@ struct exynos_tmu_platform_data { u8 second_point_trim; u8 default_temp_offset; - enum calibration_type cal_type; enum soc_type type; + u32 cal_type; + u32 cal_mode; struct freq_clip_table freq_tab[4]; unsigned int freq_tab_count; }; diff --git a/include/dt-bindings/thermal/thermal_exynos.h b/include/dt-bindings/thermal/thermal_exynos.h new file mode 100644 index 00000000000000..0646500bca697f --- /dev/null +++ b/include/dt-bindings/thermal/thermal_exynos.h @@ -0,0 +1,28 @@ +/* + * thermal_exynos.h - Samsung EXYNOS TMU device tree definitions + * + * Copyright (C) 2014 Samsung Electronics + * Lukasz Majewski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 _EXYNOS_THERMAL_TMU_DT_H +#define _EXYNOS_THERMAL_TMU_DT_H + +#define TYPE_ONE_POINT_TRIMMING 0 +#define TYPE_ONE_POINT_TRIMMING_25 1 +#define TYPE_ONE_POINT_TRIMMING_85 2 +#define TYPE_TWO_POINT_TRIMMING 3 +#define TYPE_NONE 4 + +#endif /* _EXYNOS_THERMAL_TMU_DT_H */ From adbe9c8a1853f604dfeb3d561df57e793f22cd81 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 23 Jan 2015 13:09:56 +0100 Subject: [PATCH 628/788] arm: dts: trats: Enable TMU on the Exynos4210 trats device The thermal IP block (Thermal Management Unit) called TMU has been enabled in this device. Signed-off-by: Lukasz Majewski --- arch/arm/boot/dts/exynos4210-trats.dts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/boot/dts/exynos4210-trats.dts b/arch/arm/boot/dts/exynos4210-trats.dts index 720836205546f2..61009f4e31451f 100644 --- a/arch/arm/boot/dts/exynos4210-trats.dts +++ b/arch/arm/boot/dts/exynos4210-trats.dts @@ -424,6 +424,10 @@ status = "okay"; }; + tmu@100C0000 { + status = "okay"; + }; + camera { pinctrl-names = "default"; pinctrl-0 = <>; From 6e991829890849ba7e7a49b286f735dbf587c5d1 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 23 Jan 2015 13:09:58 +0100 Subject: [PATCH 629/788] arm: dts: odroid: Enable TMU at Exynos4412 based Odroid U3 device This commit enables TMU IP block on the Exynos4412 Odroid U3 device. Signed-off-by: Lukasz Majewski --- arch/arm/boot/dts/exynos4412-odroid-common.dtsi | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index 8d41f9e7f22a7f..a4a6cf6a2fb180 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -445,6 +445,11 @@ i2c@138E0000 { status = "okay"; }; + + tmu@100C0000 { + vtmu-supply = <&ldo10_reg>; + status = "okay"; + }; }; &ppmu_dmc0 { From a9d19806bbb4cde535b57fe1f15dfd14ce442ae1 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 23 Jan 2015 13:09:59 +0100 Subject: [PATCH 630/788] arm: dts: Adding CPU cooling binding for Exynos SoCs Presented patch aims to move data necessary for correct CPU cooling device configuration from exynos_tmu_data.c to device tree. Signed-off-by: Lukasz Majewski --- arch/arm/boot/dts/exynos4210-trats.dts | 15 ++++++++++++++ arch/arm/boot/dts/exynos4210.dtsi | 5 ++++- arch/arm/boot/dts/exynos4212.dtsi | 5 ++++- .../boot/dts/exynos4412-odroid-common.dtsi | 15 ++++++++++++++ arch/arm/boot/dts/exynos4412-trats2.dts | 15 ++++++++++++++ arch/arm/boot/dts/exynos4412.dtsi | 5 ++++- arch/arm/boot/dts/exynos5250.dtsi | 20 ++++++++++++++++++- 7 files changed, 76 insertions(+), 4 deletions(-) diff --git a/arch/arm/boot/dts/exynos4210-trats.dts b/arch/arm/boot/dts/exynos4210-trats.dts index 61009f4e31451f..e3879c0eaf7897 100644 --- a/arch/arm/boot/dts/exynos4210-trats.dts +++ b/arch/arm/boot/dts/exynos4210-trats.dts @@ -428,6 +428,21 @@ status = "okay"; }; + thermal-zones { + cpu_thermal: cpu-thermal { + cooling-maps { + map0 { + /* Corresponds to 800MHz at freq_table */ + cooling-device = <&cpu0 2 2>; + }; + map1 { + /* Corresponds to 200MHz at freq_table */ + cooling-device = <&cpu0 4 4>; + }; + }; + }; + }; + camera { pinctrl-names = "default"; pinctrl-0 = <>; diff --git a/arch/arm/boot/dts/exynos4210.dtsi b/arch/arm/boot/dts/exynos4210.dtsi index 4890660c3d3ec9..7b04fe734a85d5 100644 --- a/arch/arm/boot/dts/exynos4210.dtsi +++ b/arch/arm/boot/dts/exynos4210.dtsi @@ -35,10 +35,13 @@ #address-cells = <1>; #size-cells = <0>; - cpu@900 { + cpu0: cpu@900 { device_type = "cpu"; compatible = "arm,cortex-a9"; reg = <0x900>; + cooling-min-level = <4>; + cooling-max-level = <2>; + #cooling-cells = <2>; /* min followed by max */ }; cpu@901 { diff --git a/arch/arm/boot/dts/exynos4212.dtsi b/arch/arm/boot/dts/exynos4212.dtsi index dd0a43ec56da90..5be03288f1ee61 100644 --- a/arch/arm/boot/dts/exynos4212.dtsi +++ b/arch/arm/boot/dts/exynos4212.dtsi @@ -26,10 +26,13 @@ #address-cells = <1>; #size-cells = <0>; - cpu@A00 { + cpu0: cpu@A00 { device_type = "cpu"; compatible = "arm,cortex-a9"; reg = <0xA00>; + cooling-min-level = <13>; + cooling-max-level = <7>; + #cooling-cells = <2>; /* min followed by max */ }; cpu@A01 { diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index a4a6cf6a2fb180..07e33c7e683b6a 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -450,6 +450,21 @@ vtmu-supply = <&ldo10_reg>; status = "okay"; }; + + thermal-zones { + cpu_thermal: cpu-thermal { + cooling-maps { + map0 { + /* Corresponds to 800MHz at freq_table */ + cooling-device = <&cpu0 7 7>; + }; + map1 { + /* Corresponds to 200MHz at freq_table */ + cooling-device = <&cpu0 13 13>; + }; + }; + }; + }; }; &ppmu_dmc0 { diff --git a/arch/arm/boot/dts/exynos4412-trats2.dts b/arch/arm/boot/dts/exynos4412-trats2.dts index 70eac857c985c2..7d3c49b2eb1796 100644 --- a/arch/arm/boot/dts/exynos4412-trats2.dts +++ b/arch/arm/boot/dts/exynos4412-trats2.dts @@ -863,6 +863,21 @@ pulldown-ohm = <100000>; /* 100K */ io-channels = <&adc 2>; /* Battery temperature */ }; + + thermal-zones { + cpu_thermal: cpu-thermal { + cooling-maps { + map0 { + /* Corresponds to 800MHz at freq_table */ + cooling-device = <&cpu0 7 7>; + }; + map1 { + /* Corresponds to 200MHz at freq_table */ + cooling-device = <&cpu0 13 13>; + }; + }; + }; + }; }; &ppmu_dmc0 { diff --git a/arch/arm/boot/dts/exynos4412.dtsi b/arch/arm/boot/dts/exynos4412.dtsi index 0f6ec93bb1d8a2..68ad43b391ae61 100644 --- a/arch/arm/boot/dts/exynos4412.dtsi +++ b/arch/arm/boot/dts/exynos4412.dtsi @@ -26,10 +26,13 @@ #address-cells = <1>; #size-cells = <0>; - cpu@A00 { + cpu0: cpu@A00 { device_type = "cpu"; compatible = "arm,cortex-a9"; reg = <0xA00>; + cooling-min-level = <13>; + cooling-max-level = <7>; + #cooling-cells = <2>; /* min followed by max */ }; cpu@A01 { diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index ae22bd98f956c4..48ce5cbac8c590 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -58,11 +58,14 @@ #address-cells = <1>; #size-cells = <0>; - cpu@0 { + cpu0: cpu@0 { device_type = "cpu"; compatible = "arm,cortex-a15"; reg = <0>; clock-frequency = <1700000000>; + cooling-min-level = <15>; + cooling-max-level = <9>; + #cooling-cells = <2>; /* min followed by max */ }; cpu@1 { device_type = "cpu"; @@ -249,6 +252,21 @@ clock-names = "tmu_apbif"; }; + thermal-zones { + cpu_thermal: cpu-thermal { + cooling-maps { + map0 { + /* Corresponds to 800MHz at freq_table */ + cooling-device = <&cpu0 9 9>; + }; + map1 { + /* Corresponds to 200MHz at freq_table */ + cooling-device = <&cpu0 15 15>; + }; + }; + }; + }; + serial@12C00000 { clocks = <&clock CLK_UART0>, <&clock CLK_SCLK_UART0>; clock-names = "uart", "clk_uart_baud0"; From d8963071f9016724f766a460139712df7fb2da6a Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 23 Jan 2015 13:10:00 +0100 Subject: [PATCH 631/788] thermal: exynos: Modify exynos thermal code to use device tree for cpu cooling configuration Up till now exynos_tmu_data.c was used for storing CPU cooling configuration data. Now the Exynos thermal core code uses device tree to get this data. For this purpose generic thermal code for configuring CPU cooling was used. Signed-off-by: Lukasz Majewski --- .../thermal/samsung/exynos_thermal_common.c | 122 ++++++++++-------- drivers/thermal/samsung/exynos_tmu.c | 7 - drivers/thermal/samsung/exynos_tmu.h | 5 - drivers/thermal/samsung/exynos_tmu_data.c | 42 +----- 4 files changed, 73 insertions(+), 103 deletions(-) diff --git a/drivers/thermal/samsung/exynos_thermal_common.c b/drivers/thermal/samsung/exynos_thermal_common.c index 6dc3815cc73f51..00aa68862a522e 100644 --- a/drivers/thermal/samsung/exynos_thermal_common.c +++ b/drivers/thermal/samsung/exynos_thermal_common.c @@ -133,47 +133,62 @@ static int exynos_get_crit_temp(struct thermal_zone_device *thermal, static int exynos_bind(struct thermal_zone_device *thermal, struct thermal_cooling_device *cdev) { - int ret = 0, i, tab_size, level; - struct freq_clip_table *tab_ptr, *clip_data; struct exynos_thermal_zone *th_zone = thermal->devdata; struct thermal_sensor_conf *data = th_zone->sensor_conf; + struct device_node *child, *gchild, *np; + struct of_phandle_args cooling_spec; + unsigned long max, state = 0; + int ret = 0, i = 0; - tab_ptr = (struct freq_clip_table *)data->cooling_data.freq_data; - tab_size = data->cooling_data.freq_clip_count; - - if (tab_ptr == NULL || tab_size == 0) + /* + * Below code is necessary to skip binding when cpufreq's + * frequency table is not yet initialized. + */ + cdev->ops->get_max_state(cdev, &state); + if (!state && !th_zone->cool_dev_size) { + th_zone->cool_dev_size = 1; + th_zone->cool_dev[0] = cdev; + th_zone->bind = false; return 0; + } - /* find the cooling device registered*/ - for (i = 0; i < th_zone->cool_dev_size; i++) - if (cdev == th_zone->cool_dev[i]) - break; + np = of_find_node_by_path("/thermal-zones/cpu-thermal"); + if (!np) { + pr_err("failed to find thmerla-zones/cpu-thermal node\n"); + return -ENOENT; + } - /* No matching cooling device */ - if (i == th_zone->cool_dev_size) - return 0; + child = of_get_child_by_name(np, "cooling-maps"); - /* Bind the thermal zone to the cpufreq cooling device */ - for (i = 0; i < tab_size; i++) { - clip_data = (struct freq_clip_table *)&(tab_ptr[i]); - level = cpufreq_cooling_get_level(0, clip_data->freq_clip_max); - if (level == THERMAL_CSTATE_INVALID) - return 0; - switch (GET_ZONE(i)) { - case MONITOR_ZONE: - case WARN_ZONE: - if (thermal_zone_bind_cooling_device(thermal, i, cdev, - level, 0)) { - dev_err(data->dev, - "error unbinding cdev inst=%d\n", i); - ret = -EINVAL; - } - th_zone->bind = true; - break; - default: + for_each_child_of_node(child, gchild) { + ret = of_parse_phandle_with_args(gchild, "cooling-device", + "#cooling-cells", + 0, &cooling_spec); + if (ret < 0) { + pr_err("missing cooling_device property\n"); + goto end; + } + + if (cooling_spec.args_count < 2) { ret = -EINVAL; + goto end; } + + max = cooling_spec.args[0]; + if (thermal_zone_bind_cooling_device(thermal, i, cdev, + max, 0)) { + dev_err(data->dev, + "thermal error unbinding cdev inst=%d\n", i); + + ret = -EINVAL; + goto end; + } + i++; } + th_zone->bind = true; +end: + of_node_put(child); + of_node_put(np); return ret; } @@ -182,16 +197,12 @@ static int exynos_bind(struct thermal_zone_device *thermal, static int exynos_unbind(struct thermal_zone_device *thermal, struct thermal_cooling_device *cdev) { - int ret = 0, i, tab_size; + int ret = 0, i; struct exynos_thermal_zone *th_zone = thermal->devdata; struct thermal_sensor_conf *data = th_zone->sensor_conf; + struct device_node *child, *gchild, *np; - if (th_zone->bind == false) - return 0; - - tab_size = data->cooling_data.freq_clip_count; - - if (tab_size == 0) + if (th_zone->bind == false || !th_zone->cool_dev_size) return 0; /* find the cooling device registered*/ @@ -203,23 +214,30 @@ static int exynos_unbind(struct thermal_zone_device *thermal, if (i == th_zone->cool_dev_size) return 0; - /* Bind the thermal zone to the cpufreq cooling device */ - for (i = 0; i < tab_size; i++) { - switch (GET_ZONE(i)) { - case MONITOR_ZONE: - case WARN_ZONE: - if (thermal_zone_unbind_cooling_device(thermal, i, - cdev)) { - dev_err(data->dev, - "error unbinding cdev inst=%d\n", i); - ret = -EINVAL; - } - th_zone->bind = false; - break; - default: + np = of_find_node_by_path("/thermal-zones/cpu-thermal"); + if (!np) { + pr_err("failed to find thmerla-zones/cpu-thermal node\n"); + return -ENOENT; + } + + child = of_get_child_by_name(np, "cooling-maps"); + + i = 0; + for_each_child_of_node(child, gchild) { + if (thermal_zone_unbind_cooling_device(thermal, i, + cdev)) { + dev_err(data->dev, + "error unbinding cdev inst=%d\n", i); ret = -EINVAL; + goto end; } + i++; } + th_zone->bind = false; +end: + of_node_put(child); + of_node_put(np); + return ret; } diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c index 5000727c8c2b87..ae30f6af05e018 100644 --- a/drivers/thermal/samsung/exynos_tmu.c +++ b/drivers/thermal/samsung/exynos_tmu.c @@ -916,13 +916,6 @@ static int exynos_tmu_probe(struct platform_device *pdev) sensor_conf->trip_data.trigger_falling = pdata->threshold_falling; - sensor_conf->cooling_data.freq_clip_count = pdata->freq_tab_count; - for (i = 0; i < pdata->freq_tab_count; i++) { - sensor_conf->cooling_data.freq_data[i].freq_clip_max = - pdata->freq_tab[i].freq_clip_max; - sensor_conf->cooling_data.freq_data[i].temp_level = - pdata->freq_tab[i].temp_level; - } sensor_conf->dev = &pdev->dev; /* Register the sensor with thermal management interface */ ret = exynos_register_thermal(sensor_conf); diff --git a/drivers/thermal/samsung/exynos_tmu.h b/drivers/thermal/samsung/exynos_tmu.h index 7f880d2e53f56a..627dec92ec1bfb 100644 --- a/drivers/thermal/samsung/exynos_tmu.h +++ b/drivers/thermal/samsung/exynos_tmu.h @@ -83,9 +83,6 @@ enum soc_type { * @second_point_trim: temp value of the second point trimming * @default_temp_offset: default temperature offset in case of no trimming * @cal_type: calibration type for temperature - * @freq_clip_table: Table representing frequency reduction percentage. - * @freq_tab_count: Count of the above table as frequency reduction may - * applicable to only some of the trigger levels. * * This structure is required for configuration of exynos_tmu driver. */ @@ -111,8 +108,6 @@ struct exynos_tmu_platform_data { enum soc_type type; u32 cal_type; u32 cal_mode; - struct freq_clip_table freq_tab[4]; - unsigned int freq_tab_count; }; /** diff --git a/drivers/thermal/samsung/exynos_tmu_data.c b/drivers/thermal/samsung/exynos_tmu_data.c index b23910069f6877..a993f3d33f9b6a 100644 --- a/drivers/thermal/samsung/exynos_tmu_data.c +++ b/drivers/thermal/samsung/exynos_tmu_data.c @@ -47,15 +47,6 @@ struct exynos_tmu_init_data const exynos4210_default_tmu_data = { .first_point_trim = 25, .second_point_trim = 85, .default_temp_offset = 50, - .freq_tab[0] = { - .freq_clip_max = 800 * 1000, - .temp_level = 85, - }, - .freq_tab[1] = { - .freq_clip_max = 200 * 1000, - .temp_level = 100, - }, - .freq_tab_count = 2, .type = SOC_ARCH_EXYNOS4210, }, }, @@ -87,16 +78,7 @@ struct exynos_tmu_init_data const exynos4210_default_tmu_data = { .max_efuse_value = 100, \ .first_point_trim = 25, \ .second_point_trim = 85, \ - .default_temp_offset = 50, \ - .freq_tab[0] = { \ - .freq_clip_max = 800 * 1000, \ - .temp_level = 70, \ - }, \ - .freq_tab[1] = { \ - .freq_clip_max = 400 * 1000, \ - .temp_level = 95, \ - }, \ - .freq_tab_count = 2 + .default_temp_offset = 50 struct exynos_tmu_init_data const exynos3250_default_tmu_data = { .tmu_data = { @@ -133,16 +115,7 @@ struct exynos_tmu_init_data const exynos3250_default_tmu_data = { .max_efuse_value = 100, \ .first_point_trim = 25, \ .second_point_trim = 85, \ - .default_temp_offset = 50, \ - .freq_tab[0] = { \ - .freq_clip_max = 1400 * 1000, \ - .temp_level = 70, \ - }, \ - .freq_tab[1] = { \ - .freq_clip_max = 400 * 1000, \ - .temp_level = 95, \ - }, \ - .freq_tab_count = 2 + .default_temp_offset = 50 struct exynos_tmu_init_data const exynos4412_default_tmu_data = { .tmu_data = { @@ -189,16 +162,7 @@ struct exynos_tmu_init_data const exynos5250_default_tmu_data = { .max_efuse_value = 100, \ .first_point_trim = 25, \ .second_point_trim = 85, \ - .default_temp_offset = 50, \ - .freq_tab[0] = { \ - .freq_clip_max = 800 * 1000, \ - .temp_level = 85, \ - }, \ - .freq_tab[1] = { \ - .freq_clip_max = 200 * 1000, \ - .temp_level = 103, \ - }, \ - .freq_tab_count = 2, \ + .default_temp_offset = 50, #define EXYNOS5260_TMU_DATA \ __EXYNOS5260_TMU_DATA \ From a4dd1e199b64010d67399725b187f392848a6060 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 23 Jan 2015 13:14:20 +0100 Subject: [PATCH 632/788] cpufreq: exynos: Use device tree to determine if cpufreq cooling should be registered With thermal subsystem rework it is necessary to tune current cpufreq code to use cpu frequency change as a potential cooling device. Now the cpu cooling device is registered only when proper nodes and properties are available in device tree. Lack of them, however, will not prevent cpufreq for normal operation. Signed-off-by: Lukasz Majewski --- drivers/cpufreq/exynos-cpufreq.c | 33 +++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c index f99a0b0b7c06ac..5e98c6b1f284b6 100644 --- a/drivers/cpufreq/exynos-cpufreq.c +++ b/drivers/cpufreq/exynos-cpufreq.c @@ -18,10 +18,13 @@ #include #include #include +#include +#include #include "exynos-cpufreq.h" static struct exynos_dvfs_info *exynos_info; +static struct thermal_cooling_device *cdev; static struct regulator *arm_regulator; static unsigned int locking_frequency; @@ -156,6 +159,7 @@ static struct cpufreq_driver exynos_driver = { static int exynos_cpufreq_probe(struct platform_device *pdev) { + struct device_node *cpus, *np; int ret = -EINVAL; exynos_info = kzalloc(sizeof(*exynos_info), GFP_KERNEL); @@ -198,9 +202,36 @@ static int exynos_cpufreq_probe(struct platform_device *pdev) /* Done here as we want to capture boot frequency */ locking_frequency = clk_get_rate(exynos_info->cpu_clk) / 1000; - if (!cpufreq_register_driver(&exynos_driver)) + ret = cpufreq_register_driver(&exynos_driver); + if (ret) + goto err_cpufreq_reg; + + cpus = of_find_node_by_path("/cpus"); + if (!cpus) { + pr_err("failed to find cpus node\n"); + return 0; + } + + np = of_get_next_child(cpus, NULL); + if (!np) { + pr_err("failed to find cpus child node\n"); + of_node_put(cpus); return 0; + } + + if (of_find_property(np, "#cooling-cells", NULL)) { + cdev = of_cpufreq_cooling_register(np, + cpu_present_mask); + if (IS_ERR(cdev)) + pr_err("running cpufreq without cooling device: %ld\n", + PTR_ERR(cdev)); + } + of_node_put(np); + of_node_put(cpus); + + return 0; +err_cpufreq_reg: dev_err(&pdev->dev, "failed to register cpufreq driver\n"); regulator_put(arm_regulator); err_vdd_arm: From d02c8d5b341a66357f369c4e2e00438b69f1f278 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 23 Jan 2015 13:10:01 +0100 Subject: [PATCH 633/788] thermal: exynos: dts: Add default definition of the TMU sensor parameter Exynos 4 and 5 family of SoCs uses almost identical TMU sensor to measure the on chip temperature. For this reason it is possible to group TMU configuration parameters in one dts file. Signed-off-by: Lukasz Majewski --- .../boot/dts/exynos4412-tmu-sensor-conf.dtsi | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 arch/arm/boot/dts/exynos4412-tmu-sensor-conf.dtsi diff --git a/arch/arm/boot/dts/exynos4412-tmu-sensor-conf.dtsi b/arch/arm/boot/dts/exynos4412-tmu-sensor-conf.dtsi new file mode 100644 index 00000000000000..e3f7934d19d0ab --- /dev/null +++ b/arch/arm/boot/dts/exynos4412-tmu-sensor-conf.dtsi @@ -0,0 +1,24 @@ +/* + * Device tree sources for Exynos4412 TMU sensor configuration + * + * Copyright (c) 2014 Lukasz Majewski + * + * 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. + * + */ + +#include + +#thermal-sensor-cells = <0>; +samsung,tmu_gain = <8>; +samsung,tmu_reference_voltage = <16>; +samsung,tmu_noise_cancel_mode = <4>; +samsung,tmu_efuse_value = <55>; +samsung,tmu_min_efuse_value = <40>; +samsung,tmu_max_efuse_value = <100>; +samsung,tmu_first_point_trim = <25>; +samsung,tmu_second_point_trim = <85>; +samsung,tmu_default_temp_offset = <50>; +samsung,tmu_cal_type = ; From c02c890b21e2aab6a5133da8bdd150b6e7a34f34 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 23 Jan 2015 13:10:02 +0100 Subject: [PATCH 634/788] dts: Documentation: Extending documentation entry for exynos-thermal Properties necessary for providing Exynos thermal configuration via device tree. Signed-off-by: Lukasz Majewski --- .../bindings/thermal/exynos-thermal.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Documentation/devicetree/bindings/thermal/exynos-thermal.txt b/Documentation/devicetree/bindings/thermal/exynos-thermal.txt index ae738f562acca6..849779415970f9 100644 --- a/Documentation/devicetree/bindings/thermal/exynos-thermal.txt +++ b/Documentation/devicetree/bindings/thermal/exynos-thermal.txt @@ -39,6 +39,18 @@ - vtmu-supply: This entry is optional and provides the regulator node supplying voltage to TMU. If needed this entry can be placed inside board/platform specific dts file. +Following properties are mandatory (depending on SoC): +- samsung,tmu_gain: Gain value for internal TMU operation. +- samsung,tmu_reference_voltage: Value of TMU IP block's reference voltage +- samsung,tmu_noise_cancel_mode: Mode for noise cancellation +- samsung,tmu_efuse_value: Default level of temperature - it is needed when + in factory fusing produced wrong value +- samsung,tmu_min_efuse_value: Minimum temperature fused value +- samsung,tmu_max_efuse_value: Maximum temperature fused value +- samsung,tmu_first_point_trim: First point trimming value +- samsung,tmu_second_point_trim: Second point trimming value +- samsung,tmu_default_temp_offset: Default temperature offset +- samsung,tmu_cal_type: Callibration type Example 1): @@ -51,6 +63,7 @@ Example 1): clock-names = "tmu_apbif"; status = "disabled"; vtmu-supply = <&tmu_regulator_node>; + #include "exynos4412-tmu-sensor-conf.dtsi" }; Example 2): @@ -70,6 +83,7 @@ Example 3): (In case of Exynos5420 "with misplaced TRIMINFO register") interrupts = <0 184 0>; clocks = <&clock 318>, <&clock 318>; clock-names = "tmu_apbif", "tmu_triminfo_apbif"; + #include "exynos4412-tmu-sensor-conf.dtsi" }; tmu_cpu3: tmu@1006c000 { @@ -78,6 +92,7 @@ Example 3): (In case of Exynos5420 "with misplaced TRIMINFO register") interrupts = <0 185 0>; clocks = <&clock 318>, <&clock 319>; clock-names = "tmu_apbif", "tmu_triminfo_apbif"; + #include "exynos4412-tmu-sensor-conf.dtsi" }; tmu_gpu: tmu@100a0000 { @@ -86,6 +101,7 @@ Example 3): (In case of Exynos5420 "with misplaced TRIMINFO register") interrupts = <0 215 0>; clocks = <&clock 319>, <&clock 318>; clock-names = "tmu_apbif", "tmu_triminfo_apbif"; + #include "exynos4412-tmu-sensor-conf.dtsi" }; Note: For multi-instance tmu each instance should have an alias correctly From 60f6d38e1e56824cdb91ecb0db2066c3b25e4985 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 23 Jan 2015 13:10:03 +0100 Subject: [PATCH 635/788] thermal: dts: Default trip points definition for Exynos5420 SoCs This code groups in one place default settings of trip points. It is used in SoCs with multiple instances of TMU sensor. Separate device tree file prevents from multiple copying of the same data. Signed-off-by: Lukasz Majewski --- arch/arm/boot/dts/exynos5420-trip-points.dtsi | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 arch/arm/boot/dts/exynos5420-trip-points.dtsi diff --git a/arch/arm/boot/dts/exynos5420-trip-points.dtsi b/arch/arm/boot/dts/exynos5420-trip-points.dtsi new file mode 100644 index 00000000000000..09d6c56b99e421 --- /dev/null +++ b/arch/arm/boot/dts/exynos5420-trip-points.dtsi @@ -0,0 +1,35 @@ +/* + * Device tree sources for default Exynos 5420 thermal zone definition + * + * Copyright (c) 2014 Lukasz Majewski + * + * 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. + * + */ + +polling-delay-passive = <0>; +polling-delay = <0>; +trips { + cpu-alert-0 { + temperature = <85000>; /* millicelsius */ + hysteresis = <10000>; /* millicelsius */ + type = "active"; + }; + cpu-alert-1 { + temperature = <103000>; /* millicelsius */ + hysteresis = <10000>; /* millicelsius */ + type = "active"; + }; + cpu-alert-2 { + temperature = <110000>; /* millicelsius */ + hysteresis = <10000>; /* millicelsius */ + type = "active"; + }; + cpu-crit-0 { + temperature = <1200000>; /* millicelsius */ + hysteresis = <0>; /* millicelsius */ + type = "critical"; + }; +}; From 69bdf34a0bd7452f763368217f1aaba794bdd6da Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 23 Jan 2015 13:10:04 +0100 Subject: [PATCH 636/788] thermal: exynos: dts: Define default thermal-zones for Exynos4 Trip points corresponding to the one defined in the exynos_tmu_data.c for Exynos4 have been included. This thermal-zones attribute is afterwards reused for Exynos4210, Exynos4412 and Exynos5250. Signed-off-by: Lukasz Majewski --- arch/arm/boot/dts/exynos4-cpu-thermal.dtsi | 52 ++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 arch/arm/boot/dts/exynos4-cpu-thermal.dtsi diff --git a/arch/arm/boot/dts/exynos4-cpu-thermal.dtsi b/arch/arm/boot/dts/exynos4-cpu-thermal.dtsi new file mode 100644 index 00000000000000..506600a0966e9c --- /dev/null +++ b/arch/arm/boot/dts/exynos4-cpu-thermal.dtsi @@ -0,0 +1,52 @@ +/* + * Device tree sources for Exynos4 thermal zone + * + * Copyright (c) 2014 Lukasz Majewski + * + * 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. + * + */ + +#include + +/ { + thermal-zones { + cpu_thermal: cpu-thermal { + thermal-sensors = <&tmu 0>; + polling-delay-passive = <0>; + polling-delay = <0>; + trips { + cpu_alert0: cpu-alert-0 { + temperature = <70000>; /* millicelsius */ + hysteresis = <10000>; /* millicelsius */ + type = "active"; + }; + cpu_alert1: cpu-alert-1 { + temperature = <95000>; /* millicelsius */ + hysteresis = <10000>; /* millicelsius */ + type = "active"; + }; + cpu_alert2: cpu-alert-2 { + temperature = <110000>; /* millicelsius */ + hysteresis = <10000>; /* millicelsius */ + type = "active"; + }; + cpu_crit0: cpu-crit-0 { + temperature = <120000>; /* millicelsius */ + hysteresis = <0>; /* millicelsius */ + type = "critical"; + }; + }; + cooling-maps { + map0 { + trip = <&cpu_alert0>; + }; + map1 { + trip = <&cpu_alert1>; + }; + }; + }; + }; +}; From f43bb70e1dbd9d4889a3823f5e9015b367694b90 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 23 Jan 2015 13:10:05 +0100 Subject: [PATCH 637/788] thermal: dts: exynos: Trip points and sensor configuration data for Exynos5440 This commit provides information about Exynos5440 device configuration. Previously this information was available in exynos_tmu_data.c file. Now it is available in the device tree. Such approach allows reusing some common code for thermal. Signed-off-by: Lukasz Majewski --- .../boot/dts/exynos5440-tmu-sensor-conf.dtsi | 24 ++++++++++++++++++ arch/arm/boot/dts/exynos5440-trip-points.dtsi | 25 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 arch/arm/boot/dts/exynos5440-tmu-sensor-conf.dtsi create mode 100644 arch/arm/boot/dts/exynos5440-trip-points.dtsi diff --git a/arch/arm/boot/dts/exynos5440-tmu-sensor-conf.dtsi b/arch/arm/boot/dts/exynos5440-tmu-sensor-conf.dtsi new file mode 100644 index 00000000000000..7b2fba0ae92b11 --- /dev/null +++ b/arch/arm/boot/dts/exynos5440-tmu-sensor-conf.dtsi @@ -0,0 +1,24 @@ +/* + * Device tree sources for Exynos5440 TMU sensor configuration + * + * Copyright (c) 2014 Lukasz Majewski + * + * 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. + * + */ + +#include + +#thermal-sensor-cells = <0>; +samsung,tmu_gain = <5>; +samsung,tmu_reference_voltage = <16>; +samsung,tmu_noise_cancel_mode = <4>; +samsung,tmu_efuse_value = <0x5d2d>; +samsung,tmu_min_efuse_value = <16>; +samsung,tmu_max_efuse_value = <76>; +samsung,tmu_first_point_trim = <25>; +samsung,tmu_second_point_trim = <70>; +samsung,tmu_default_temp_offset = <25>; +samsung,tmu_cal_type = ; diff --git a/arch/arm/boot/dts/exynos5440-trip-points.dtsi b/arch/arm/boot/dts/exynos5440-trip-points.dtsi new file mode 100644 index 00000000000000..48adfa8f4300b5 --- /dev/null +++ b/arch/arm/boot/dts/exynos5440-trip-points.dtsi @@ -0,0 +1,25 @@ +/* + * Device tree sources for default Exynos5440 thermal zone definition + * + * Copyright (c) 2014 Lukasz Majewski + * + * 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. + * + */ + +polling-delay-passive = <0>; +polling-delay = <0>; +trips { + cpu-alert-0 { + temperature = <100000>; /* millicelsius */ + hysteresis = <0>; /* millicelsius */ + type = "active"; + }; + cpu-crit-0 { + temperature = <1050000>; /* millicelsius */ + hysteresis = <0>; /* millicelsius */ + type = "critical"; + }; +}; From 4e6d12d5bd0640b0a79c1eaaa96ab9727c2e0006 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 23 Jan 2015 13:10:06 +0100 Subject: [PATCH 638/788] dts: Documentation: Update exynos-thermal.txt example for Exynos5440 Updating exynos-thermal.txt documentation entry for Exynos5440 Signed-off-by: Lukasz Majewski --- Documentation/devicetree/bindings/thermal/exynos-thermal.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/thermal/exynos-thermal.txt b/Documentation/devicetree/bindings/thermal/exynos-thermal.txt index 849779415970f9..0f44932889c37d 100644 --- a/Documentation/devicetree/bindings/thermal/exynos-thermal.txt +++ b/Documentation/devicetree/bindings/thermal/exynos-thermal.txt @@ -74,6 +74,7 @@ Example 2): interrupts = <0 58 0>; clocks = <&clock 21>; clock-names = "tmu_apbif"; + #include "exynos5440-tmu-sensor-conf.dtsi" }; Example 3): (In case of Exynos5420 "with misplaced TRIMINFO register") From 0e01845b0d49e14de16a2a809ed7ffced6d5df52 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 23 Jan 2015 13:10:07 +0100 Subject: [PATCH 639/788] thermal: exynos: dts: Provide device tree bindings identical to the one in exynos_tmu_data.c Presented device tree bindings provide data already hardcoded in the exynos_tmu_data.c file. After this commit, it should be possible to reuse common thermal core framework in Exynos SoCs. Signed-off-by: Lukasz Majewski --- arch/arm/boot/dts/exynos3250.dtsi | 2 ++ arch/arm/boot/dts/exynos4.dtsi | 4 ++++ arch/arm/boot/dts/exynos4210.dtsi | 25 ++++++++++++++++++++++++- arch/arm/boot/dts/exynos4x12.dtsi | 1 + arch/arm/boot/dts/exynos5250.dtsi | 9 +++++++-- arch/arm/boot/dts/exynos5420.dtsi | 28 ++++++++++++++++++++++++++++ arch/arm/boot/dts/exynos5440.dtsi | 18 ++++++++++++++++++ 7 files changed, 84 insertions(+), 3 deletions(-) diff --git a/arch/arm/boot/dts/exynos3250.dtsi b/arch/arm/boot/dts/exynos3250.dtsi index d7df2fb07bcb91..bea31b8312e1dd 100644 --- a/arch/arm/boot/dts/exynos3250.dtsi +++ b/arch/arm/boot/dts/exynos3250.dtsi @@ -18,6 +18,7 @@ */ #include "skeleton.dtsi" +#include "exynos4-cpu-thermal.dtsi" #include / { @@ -193,6 +194,7 @@ interrupts = <0 216 0>; clocks = <&cmu CLK_TMU_APBIF>; clock-names = "tmu_apbif"; + #include "exynos4412-tmu-sensor-conf.dtsi" status = "disabled"; }; diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi index adc3d54105f619..e4a3e8b8c43915 100644 --- a/arch/arm/boot/dts/exynos4.dtsi +++ b/arch/arm/boot/dts/exynos4.dtsi @@ -818,4 +818,8 @@ clock-names = "ppmu"; status = "disabled"; }; + + tmu: tmu@100C0000 { + #include "exynos4412-tmu-sensor-conf.dtsi" + }; }; diff --git a/arch/arm/boot/dts/exynos4210.dtsi b/arch/arm/boot/dts/exynos4210.dtsi index 7b04fe734a85d5..be89f83f70e775 100644 --- a/arch/arm/boot/dts/exynos4210.dtsi +++ b/arch/arm/boot/dts/exynos4210.dtsi @@ -21,6 +21,7 @@ #include "exynos4.dtsi" #include "exynos4210-pinctrl.dtsi" +#include "exynos4-cpu-thermal.dtsi" / { compatible = "samsung,exynos4210", "samsung,exynos4"; @@ -156,16 +157,38 @@ reg = <0x03860000 0x1000>; }; - tmu@100C0000 { + tmu: tmu@100C0000 { compatible = "samsung,exynos4210-tmu"; interrupt-parent = <&combiner>; reg = <0x100C0000 0x100>; interrupts = <2 4>; clocks = <&clock CLK_TMU_APBIF>; clock-names = "tmu_apbif"; + samsung,tmu_gain = <15>; + samsung,tmu_reference_voltage = <7>; status = "disabled"; }; + thermal-zones { + cpu_thermal: cpu-thermal { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tmu 0>; + + trips { + cpu_alert0: cpu-alert-0 { + temperature = <85000>; /* millicelsius */ + }; + cpu_alert1: cpu-alert-1 { + temperature = <100000>; /* millicelsius */ + }; + cpu_alert2: cpu-alert-2 { + temperature = <110000>; /* millicelsius */ + }; + }; + }; + }; + g2d@12800000 { compatible = "samsung,s5pv210-g2d"; reg = <0x12800000 0x1000>; diff --git a/arch/arm/boot/dts/exynos4x12.dtsi b/arch/arm/boot/dts/exynos4x12.dtsi index d97e018e61e143..6a6abe14fd9b59 100644 --- a/arch/arm/boot/dts/exynos4x12.dtsi +++ b/arch/arm/boot/dts/exynos4x12.dtsi @@ -19,6 +19,7 @@ #include "exynos4.dtsi" #include "exynos4x12-pinctrl.dtsi" +#include "exynos4-cpu-thermal.dtsi" / { aliases { diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index 48ce5cbac8c590..adbde1adad95dd 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -20,7 +20,7 @@ #include #include "exynos5.dtsi" #include "exynos5250-pinctrl.dtsi" - +#include "exynos4-cpu-thermal.dtsi" #include / { @@ -244,16 +244,21 @@ status = "disabled"; }; - tmu@10060000 { + tmu: tmu@10060000 { compatible = "samsung,exynos5250-tmu"; reg = <0x10060000 0x100>; interrupts = <0 65 0>; clocks = <&clock CLK_TMU>; clock-names = "tmu_apbif"; + #include "exynos4412-tmu-sensor-conf.dtsi" }; thermal-zones { cpu_thermal: cpu-thermal { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tmu 0>; + cooling-maps { map0 { /* Corresponds to 800MHz at freq_table */ diff --git a/arch/arm/boot/dts/exynos5420.dtsi b/arch/arm/boot/dts/exynos5420.dtsi index 8e464314260c44..c41a398afa2914 100644 --- a/arch/arm/boot/dts/exynos5420.dtsi +++ b/arch/arm/boot/dts/exynos5420.dtsi @@ -766,6 +766,7 @@ interrupts = <0 65 0>; clocks = <&clock CLK_TMU>; clock-names = "tmu_apbif"; + #include "exynos4412-tmu-sensor-conf.dtsi" }; tmu_cpu1: tmu@10064000 { @@ -774,6 +775,7 @@ interrupts = <0 183 0>; clocks = <&clock CLK_TMU>; clock-names = "tmu_apbif"; + #include "exynos4412-tmu-sensor-conf.dtsi" }; tmu_cpu2: tmu@10068000 { @@ -782,6 +784,7 @@ interrupts = <0 184 0>; clocks = <&clock CLK_TMU>, <&clock CLK_TMU>; clock-names = "tmu_apbif", "tmu_triminfo_apbif"; + #include "exynos4412-tmu-sensor-conf.dtsi" }; tmu_cpu3: tmu@1006c000 { @@ -790,6 +793,7 @@ interrupts = <0 185 0>; clocks = <&clock CLK_TMU>, <&clock CLK_TMU_GPU>; clock-names = "tmu_apbif", "tmu_triminfo_apbif"; + #include "exynos4412-tmu-sensor-conf.dtsi" }; tmu_gpu: tmu@100a0000 { @@ -798,6 +802,30 @@ interrupts = <0 215 0>; clocks = <&clock CLK_TMU_GPU>, <&clock CLK_TMU>; clock-names = "tmu_apbif", "tmu_triminfo_apbif"; + #include "exynos4412-tmu-sensor-conf.dtsi" + }; + + thermal-zones { + cpu0_thermal: cpu0-thermal { + thermal-sensors = <&tmu_cpu0>; + #include "exynos5420-trip-points.dtsi" + }; + cpu1_thermal: cpu1-thermal { + thermal-sensors = <&tmu_cpu1>; + #include "exynos5420-trip-points.dtsi" + }; + cpu2_thermal: cpu2-thermal { + thermal-sensors = <&tmu_cpu2>; + #include "exynos5420-trip-points.dtsi" + }; + cpu3_thermal: cpu3-thermal { + thermal-sensors = <&tmu_cpu3>; + #include "exynos5420-trip-points.dtsi" + }; + gpu_thermal: gpu-thermal { + thermal-sensors = <&tmu_gpu>; + #include "exynos5420-trip-points.dtsi" + }; }; watchdog: watchdog@101D0000 { diff --git a/arch/arm/boot/dts/exynos5440.dtsi b/arch/arm/boot/dts/exynos5440.dtsi index 8f3373cd7b878b..59d9416b3b03f0 100644 --- a/arch/arm/boot/dts/exynos5440.dtsi +++ b/arch/arm/boot/dts/exynos5440.dtsi @@ -219,6 +219,7 @@ interrupts = <0 58 0>; clocks = <&clock CLK_B_125>; clock-names = "tmu_apbif"; + #include "exynos5440-tmu-sensor-conf.dtsi" }; tmuctrl_1: tmuctrl@16011C { @@ -227,6 +228,7 @@ interrupts = <0 58 0>; clocks = <&clock CLK_B_125>; clock-names = "tmu_apbif"; + #include "exynos5440-tmu-sensor-conf.dtsi" }; tmuctrl_2: tmuctrl@160120 { @@ -235,6 +237,22 @@ interrupts = <0 58 0>; clocks = <&clock CLK_B_125>; clock-names = "tmu_apbif"; + #include "exynos5440-tmu-sensor-conf.dtsi" + }; + + thermal-zones { + cpu0_thermal: cpu0-thermal { + thermal-sensors = <&tmuctrl_0>; + #include "exynos5440-trip-points.dtsi" + }; + cpu1_thermal: cpu1-thermal { + thermal-sensors = <&tmuctrl_1>; + #include "exynos5440-trip-points.dtsi" + }; + cpu2_thermal: cpu2-thermal { + thermal-sensors = <&tmuctrl_2>; + #include "exynos5440-trip-points.dtsi" + }; }; sata@210000 { From f774570e5004d08b3bc168b0320cc3f88f5d571d Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 23 Jan 2015 13:10:08 +0100 Subject: [PATCH 640/788] thermal: samsung: core: Exynos TMU rework to use device tree for configuration This patch brings support for providing configuration via device tree. Previously this data has been hardcoded in the exynos_tmu_data.c file. Such approach was not scalable and very often required copying the whole data. Signed-off-by: Lukasz Majewski --- drivers/thermal/samsung/Makefile | 2 - drivers/thermal/samsung/exynos_tmu.c | 339 +++++++++++++++++---------- drivers/thermal/samsung/exynos_tmu.h | 61 +---- 3 files changed, 220 insertions(+), 182 deletions(-) diff --git a/drivers/thermal/samsung/Makefile b/drivers/thermal/samsung/Makefile index c09d83095dc2a7..1e47d0d89ce06e 100644 --- a/drivers/thermal/samsung/Makefile +++ b/drivers/thermal/samsung/Makefile @@ -3,5 +3,3 @@ # obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o exynos_thermal-y := exynos_tmu.o -exynos_thermal-y += exynos_tmu_data.o -exynos_thermal-$(CONFIG_EXYNOS_THERMAL_CORE) += exynos_thermal_common.o diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c index ae30f6af05e018..864eec8f5164f0 100644 --- a/drivers/thermal/samsung/exynos_tmu.c +++ b/drivers/thermal/samsung/exynos_tmu.c @@ -1,6 +1,10 @@ /* * exynos_tmu.c - Samsung EXYNOS TMU (Thermal Management Unit) * + * Copyright (C) 2014 Samsung Electronics + * Bartlomiej Zolnierkiewicz + * Lukasz Majewski + * * Copyright (C) 2011 Samsung Electronics * Donggeun Kim * Amit Daniel Kachhap @@ -31,8 +35,8 @@ #include #include -#include "exynos_thermal_common.h" #include "exynos_tmu.h" +#include "../thermal_core.h" /* Exynos generic registers */ #define EXYNOS_TMU_REG_TRIMINFO 0x0 @@ -115,6 +119,7 @@ #define EXYNOS5440_TMU_TH_RISE4_SHIFT 24 #define EXYNOS5440_EFUSE_SWAP_OFFSET 8 +#define MCELSIUS 1000 /** * struct exynos_tmu_data : A structure to hold the private data of the TMU driver @@ -150,7 +155,8 @@ struct exynos_tmu_data { struct clk *clk, *clk_sec; u8 temp_error1, temp_error2; struct regulator *regulator; - struct thermal_sensor_conf *reg_conf; + struct thermal_zone_device *tzd; + int (*tmu_initialize)(struct platform_device *pdev); void (*tmu_control)(struct platform_device *pdev, bool on); int (*tmu_read)(struct exynos_tmu_data *data); @@ -159,6 +165,33 @@ struct exynos_tmu_data { void (*tmu_clear_irqs)(struct exynos_tmu_data *data); }; +static void exynos_report_trigger(struct exynos_tmu_data *p) +{ + char data[10], *envp[] = { data, NULL }; + struct thermal_zone_device *tz = p->tzd; + unsigned long temp; + unsigned int i; + + if (!p) { + pr_err("Wrong temperature configuration data\n"); + return; + } + + thermal_zone_device_update(tz); + + mutex_lock(&tz->lock); + /* Find the level for which trip happened */ + for (i = 0; i < of_thermal_get_ntrips(tz); i++) { + tz->ops->get_trip_temp(tz, i, &temp); + if (tz->last_temperature < temp) + break; + } + + snprintf(data, sizeof(data), "%u", i); + kobject_uevent_env(&tz->device.kobj, KOBJ_CHANGE, envp); + mutex_unlock(&tz->lock); +} + /* * TMU treats temperature as a mapped temperature code. * The temperature is converted differently depending on the calibration type. @@ -234,14 +267,25 @@ static void sanitize_temp_error(struct exynos_tmu_data *data, u32 trim_info) static u32 get_th_reg(struct exynos_tmu_data *data, u32 threshold, bool falling) { - struct exynos_tmu_platform_data *pdata = data->pdata; + struct thermal_zone_device *tz = data->tzd; + const struct thermal_trip * const trips = + of_thermal_get_trip_points(tz); + unsigned long temp; int i; - for (i = 0; i < pdata->non_hw_trigger_levels; i++) { - u8 temp = pdata->trigger_levels[i]; + if (!trips) { + pr_err("%s: Cannot get trip points from of-thermal.c!\n", + __func__); + return 0; + } + for (i = 0; i < of_thermal_get_ntrips(tz); i++) { + if (trips[i].type == THERMAL_TRIP_CRITICAL) + continue; + + temp = trips[i].temperature / MCELSIUS; if (falling) - temp -= pdata->threshold_falling; + temp -= (trips[i].hysteresis / MCELSIUS); else threshold &= ~(0xff << 8 * i); @@ -305,9 +349,19 @@ static void exynos_tmu_control(struct platform_device *pdev, bool on) static int exynos4210_tmu_initialize(struct platform_device *pdev) { struct exynos_tmu_data *data = platform_get_drvdata(pdev); - struct exynos_tmu_platform_data *pdata = data->pdata; - unsigned int status; + struct thermal_zone_device *tz = data->tzd; + const struct thermal_trip * const trips = + of_thermal_get_trip_points(tz); int ret = 0, threshold_code, i; + unsigned long reference, temp; + unsigned int status; + + if (!trips) { + pr_err("%s: Cannot get trip points from of-thermal.c!\n", + __func__); + ret = -ENODEV; + goto out; + } status = readb(data->base + EXYNOS_TMU_REG_STATUS); if (!status) { @@ -318,12 +372,19 @@ static int exynos4210_tmu_initialize(struct platform_device *pdev) sanitize_temp_error(data, readl(data->base + EXYNOS_TMU_REG_TRIMINFO)); /* Write temperature code for threshold */ - threshold_code = temp_to_code(data, pdata->threshold); + reference = trips[0].temperature / MCELSIUS; + threshold_code = temp_to_code(data, reference); + if (threshold_code < 0) { + ret = threshold_code; + goto out; + } writeb(threshold_code, data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP); - for (i = 0; i < pdata->non_hw_trigger_levels; i++) - writeb(pdata->trigger_levels[i], data->base + + for (i = 0; i < of_thermal_get_ntrips(tz); i++) { + temp = trips[i].temperature / MCELSIUS; + writeb(temp - reference, data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0 + i * 4); + } data->tmu_clear_irqs(data); out: @@ -333,9 +394,11 @@ static int exynos4210_tmu_initialize(struct platform_device *pdev) static int exynos4412_tmu_initialize(struct platform_device *pdev) { struct exynos_tmu_data *data = platform_get_drvdata(pdev); - struct exynos_tmu_platform_data *pdata = data->pdata; + const struct thermal_trip * const trips = + of_thermal_get_trip_points(data->tzd); unsigned int status, trim_info, con, ctrl, rising_threshold; int ret = 0, threshold_code, i; + unsigned long crit_temp = 0; status = readb(data->base + EXYNOS_TMU_REG_STATUS); if (!status) { @@ -373,17 +436,29 @@ static int exynos4412_tmu_initialize(struct platform_device *pdev) data->tmu_clear_irqs(data); /* if last threshold limit is also present */ - i = pdata->max_trigger_level - 1; - if (pdata->trigger_levels[i] && pdata->trigger_type[i] == HW_TRIP) { - threshold_code = temp_to_code(data, pdata->trigger_levels[i]); - /* 1-4 level to be assigned in th0 reg */ - rising_threshold &= ~(0xff << 8 * i); - rising_threshold |= threshold_code << 8 * i; - writel(rising_threshold, data->base + EXYNOS_THD_TEMP_RISE); - con = readl(data->base + EXYNOS_TMU_REG_CONTROL); - con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT); - writel(con, data->base + EXYNOS_TMU_REG_CONTROL); + for (i = 0; i < of_thermal_get_ntrips(data->tzd); i++) { + if (trips[i].type == THERMAL_TRIP_CRITICAL) { + crit_temp = trips[i].temperature; + break; + } + } + + if (i == of_thermal_get_ntrips(data->tzd)) { + pr_err("%s: No CRITICAL trip point defined at of-thermal.c!\n", + __func__); + ret = -EINVAL; + goto out; } + + threshold_code = temp_to_code(data, crit_temp / MCELSIUS); + /* 1-4 level to be assigned in th0 reg */ + rising_threshold &= ~(0xff << 8 * i); + rising_threshold |= threshold_code << 8 * i; + writel(rising_threshold, data->base + EXYNOS_THD_TEMP_RISE); + con = readl(data->base + EXYNOS_TMU_REG_CONTROL); + con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT); + writel(con, data->base + EXYNOS_TMU_REG_CONTROL); + out: return ret; } @@ -391,9 +466,9 @@ static int exynos4412_tmu_initialize(struct platform_device *pdev) static int exynos5440_tmu_initialize(struct platform_device *pdev) { struct exynos_tmu_data *data = platform_get_drvdata(pdev); - struct exynos_tmu_platform_data *pdata = data->pdata; unsigned int trim_info = 0, con, rising_threshold; - int ret = 0, threshold_code, i; + int ret = 0, threshold_code; + unsigned long crit_temp = 0; /* * For exynos5440 soc triminfo value is swapped between TMU0 and @@ -422,9 +497,8 @@ static int exynos5440_tmu_initialize(struct platform_device *pdev) data->tmu_clear_irqs(data); /* if last threshold limit is also present */ - i = pdata->max_trigger_level - 1; - if (pdata->trigger_levels[i] && pdata->trigger_type[i] == HW_TRIP) { - threshold_code = temp_to_code(data, pdata->trigger_levels[i]); + if (!data->tzd->ops->get_crit_temp(data->tzd, &crit_temp)) { + threshold_code = temp_to_code(data, crit_temp / MCELSIUS); /* 5th level to be assigned in th2 reg */ rising_threshold = threshold_code << EXYNOS5440_TMU_TH_RISE4_SHIFT; @@ -442,7 +516,7 @@ static int exynos5440_tmu_initialize(struct platform_device *pdev) static void exynos4210_tmu_control(struct platform_device *pdev, bool on) { struct exynos_tmu_data *data = platform_get_drvdata(pdev); - struct exynos_tmu_platform_data *pdata = data->pdata; + struct thermal_zone_device *tz = data->tzd; unsigned int con, interrupt_en; con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); @@ -450,10 +524,15 @@ static void exynos4210_tmu_control(struct platform_device *pdev, bool on) if (on) { con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); interrupt_en = - pdata->trigger_enable[3] << EXYNOS_TMU_INTEN_RISE3_SHIFT | - pdata->trigger_enable[2] << EXYNOS_TMU_INTEN_RISE2_SHIFT | - pdata->trigger_enable[1] << EXYNOS_TMU_INTEN_RISE1_SHIFT | - pdata->trigger_enable[0] << EXYNOS_TMU_INTEN_RISE0_SHIFT; + (of_thermal_is_trip_valid(tz, 3) + << EXYNOS_TMU_INTEN_RISE3_SHIFT) | + (of_thermal_is_trip_valid(tz, 2) + << EXYNOS_TMU_INTEN_RISE2_SHIFT) | + (of_thermal_is_trip_valid(tz, 1) + << EXYNOS_TMU_INTEN_RISE1_SHIFT) | + (of_thermal_is_trip_valid(tz, 0) + << EXYNOS_TMU_INTEN_RISE0_SHIFT); + if (data->soc != SOC_ARCH_EXYNOS4210) interrupt_en |= interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; @@ -468,7 +547,7 @@ static void exynos4210_tmu_control(struct platform_device *pdev, bool on) static void exynos5440_tmu_control(struct platform_device *pdev, bool on) { struct exynos_tmu_data *data = platform_get_drvdata(pdev); - struct exynos_tmu_platform_data *pdata = data->pdata; + struct thermal_zone_device *tz = data->tzd; unsigned int con, interrupt_en; con = get_con_reg(data, readl(data->base + EXYNOS5440_TMU_S0_7_CTRL)); @@ -476,11 +555,16 @@ static void exynos5440_tmu_control(struct platform_device *pdev, bool on) if (on) { con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); interrupt_en = - pdata->trigger_enable[3] << EXYNOS5440_TMU_INTEN_RISE3_SHIFT | - pdata->trigger_enable[2] << EXYNOS5440_TMU_INTEN_RISE2_SHIFT | - pdata->trigger_enable[1] << EXYNOS5440_TMU_INTEN_RISE1_SHIFT | - pdata->trigger_enable[0] << EXYNOS5440_TMU_INTEN_RISE0_SHIFT; - interrupt_en |= interrupt_en << EXYNOS5440_TMU_INTEN_FALL0_SHIFT; + (of_thermal_is_trip_valid(tz, 3) + << EXYNOS5440_TMU_INTEN_RISE3_SHIFT) | + (of_thermal_is_trip_valid(tz, 2) + << EXYNOS5440_TMU_INTEN_RISE2_SHIFT) | + (of_thermal_is_trip_valid(tz, 1) + << EXYNOS5440_TMU_INTEN_RISE1_SHIFT) | + (of_thermal_is_trip_valid(tz, 0) + << EXYNOS5440_TMU_INTEN_RISE0_SHIFT); + interrupt_en |= + interrupt_en << EXYNOS5440_TMU_INTEN_FALL0_SHIFT; } else { con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); interrupt_en = 0; /* Disable all interrupts */ @@ -489,19 +573,22 @@ static void exynos5440_tmu_control(struct platform_device *pdev, bool on) writel(con, data->base + EXYNOS5440_TMU_S0_7_CTRL); } -static int exynos_tmu_read(struct exynos_tmu_data *data) +static int exynos_get_temp(void *p, long *temp) { - int ret; + struct exynos_tmu_data *data = p; + + if (!data) + return -EINVAL; mutex_lock(&data->lock); clk_enable(data->clk); - ret = data->tmu_read(data); - if (ret >= 0) - ret = code_to_temp(data, ret); + + *temp = code_to_temp(data, data->tmu_read(data)) * MCELSIUS; + clk_disable(data->clk); mutex_unlock(&data->lock); - return ret; + return 0; } #ifdef CONFIG_THERMAL_EMULATION @@ -613,7 +700,7 @@ static void exynos_tmu_work(struct work_struct *work) if (!IS_ERR(data->clk_sec)) clk_disable(data->clk_sec); - exynos_report_trigger(data->reg_conf); + exynos_report_trigger(data); mutex_lock(&data->lock); clk_enable(data->clk); @@ -673,55 +760,89 @@ static irqreturn_t exynos_tmu_irq(int irq, void *id) static const struct of_device_id exynos_tmu_match[] = { { .compatible = "samsung,exynos3250-tmu", - .data = &exynos3250_default_tmu_data, }, { .compatible = "samsung,exynos4210-tmu", - .data = &exynos4210_default_tmu_data, }, { .compatible = "samsung,exynos4412-tmu", - .data = &exynos4412_default_tmu_data, }, { .compatible = "samsung,exynos5250-tmu", - .data = &exynos5250_default_tmu_data, }, { .compatible = "samsung,exynos5260-tmu", - .data = &exynos5260_default_tmu_data, }, { .compatible = "samsung,exynos5420-tmu", - .data = &exynos5420_default_tmu_data, }, { .compatible = "samsung,exynos5420-tmu-ext-triminfo", - .data = &exynos5420_default_tmu_data, }, { .compatible = "samsung,exynos5440-tmu", - .data = &exynos5440_default_tmu_data, }, {}, }; MODULE_DEVICE_TABLE(of, exynos_tmu_match); -static inline struct exynos_tmu_platform_data *exynos_get_driver_data( - struct platform_device *pdev, int id) +static int exynos_of_get_soc_type(struct device_node *np) { - struct exynos_tmu_init_data *data_table; - struct exynos_tmu_platform_data *tmu_data; - const struct of_device_id *match; + if (of_device_is_compatible(np, "samsung,exynos3250-tmu")) + return SOC_ARCH_EXYNOS3250; + else if (of_device_is_compatible(np, "samsung,exynos4210-tmu")) + return SOC_ARCH_EXYNOS4210; + else if (of_device_is_compatible(np, "samsung,exynos4412-tmu")) + return SOC_ARCH_EXYNOS4412; + else if (of_device_is_compatible(np, "samsung,exynos5250-tmu")) + return SOC_ARCH_EXYNOS5250; + else if (of_device_is_compatible(np, "samsung,exynos5260-tmu")) + return SOC_ARCH_EXYNOS5260; + else if (of_device_is_compatible(np, "samsung,exynos5420-tmu")) + return SOC_ARCH_EXYNOS5420; + else if (of_device_is_compatible(np, + "samsung,exynos5420-tmu-ext-triminfo")) + return SOC_ARCH_EXYNOS5420_TRIMINFO; + else if (of_device_is_compatible(np, "samsung,exynos5440-tmu")) + return SOC_ARCH_EXYNOS5440; + + return -EINVAL; +} - match = of_match_node(exynos_tmu_match, pdev->dev.of_node); - if (!match) - return NULL; - data_table = (struct exynos_tmu_init_data *) match->data; - if (!data_table || id >= data_table->tmu_count) - return NULL; - tmu_data = data_table->tmu_data; - return (struct exynos_tmu_platform_data *) (tmu_data + id); +static int exynos_of_sensor_conf(struct device_node *np, + struct exynos_tmu_platform_data *pdata) +{ + u32 value; + int ret; + + of_node_get(np); + + ret = of_property_read_u32(np, "samsung,tmu_gain", &value); + pdata->gain = (u8)value; + of_property_read_u32(np, "samsung,tmu_reference_voltage", &value); + pdata->reference_voltage = (u8)value; + of_property_read_u32(np, "samsung,tmu_noise_cancel_mode", &value); + pdata->noise_cancel_mode = (u8)value; + + of_property_read_u32(np, "samsung,tmu_efuse_value", + &pdata->efuse_value); + of_property_read_u32(np, "samsung,tmu_min_efuse_value", + &pdata->min_efuse_value); + of_property_read_u32(np, "samsung,tmu_max_efuse_value", + &pdata->max_efuse_value); + + of_property_read_u32(np, "samsung,tmu_first_point_trim", &value); + pdata->first_point_trim = (u8)value; + of_property_read_u32(np, "samsung,tmu_second_point_trim", &value); + pdata->second_point_trim = (u8)value; + of_property_read_u32(np, "samsung,tmu_default_temp_offset", &value); + pdata->default_temp_offset = (u8)value; + + of_property_read_u32(np, "samsung,tmu_cal_type", &pdata->cal_type); + of_property_read_u32(np, "samsung,tmu_cal_mode", &pdata->cal_mode); + + of_node_put(np); + return 0; } static int exynos_map_dt_data(struct platform_device *pdev) @@ -771,14 +892,15 @@ static int exynos_map_dt_data(struct platform_device *pdev) return -EADDRNOTAVAIL; } - pdata = exynos_get_driver_data(pdev, data->id); - if (!pdata) { - dev_err(&pdev->dev, "No platform init data supplied.\n"); - return -ENODEV; - } + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct exynos_tmu_platform_data), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + exynos_of_sensor_conf(pdev->dev.of_node, pdata); data->pdata = pdata; - data->soc = pdata->type; + data->soc = exynos_of_get_soc_type(pdev->dev.of_node); switch (data->soc) { case SOC_ARCH_EXYNOS4210: @@ -834,12 +956,16 @@ static int exynos_map_dt_data(struct platform_device *pdev) return 0; } +static struct thermal_zone_of_device_ops exynos_sensor_ops = { + .get_temp = exynos_get_temp, + .set_emul_temp = exynos_tmu_set_emulation, +}; + static int exynos_tmu_probe(struct platform_device *pdev) { - struct exynos_tmu_data *data; struct exynos_tmu_platform_data *pdata; - struct thermal_sensor_conf *sensor_conf; - int ret, i; + struct exynos_tmu_data *data; + int ret; data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data), GFP_KERNEL); @@ -849,9 +975,15 @@ static int exynos_tmu_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); mutex_init(&data->lock); + data->tzd = thermal_zone_of_sensor_register(&pdev->dev, 0, data, + &exynos_sensor_ops); + if (IS_ERR(data->tzd)) { + pr_err("thermal: tz: %p ERROR\n", data->tzd); + return PTR_ERR(data->tzd); + } ret = exynos_map_dt_data(pdev); if (ret) - return ret; + goto err_sensor; pdata = data->pdata; @@ -860,20 +992,22 @@ static int exynos_tmu_probe(struct platform_device *pdev) data->clk = devm_clk_get(&pdev->dev, "tmu_apbif"); if (IS_ERR(data->clk)) { dev_err(&pdev->dev, "Failed to get clock\n"); - return PTR_ERR(data->clk); + ret = PTR_ERR(data->clk); + goto err_sensor; } data->clk_sec = devm_clk_get(&pdev->dev, "tmu_triminfo_apbif"); if (IS_ERR(data->clk_sec)) { if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) { dev_err(&pdev->dev, "Failed to get triminfo clock\n"); - return PTR_ERR(data->clk_sec); + ret = PTR_ERR(data->clk_sec); + goto err_sensor; } } else { ret = clk_prepare(data->clk_sec); if (ret) { dev_err(&pdev->dev, "Failed to get clock\n"); - return ret; + goto err_sensor; } } @@ -889,45 +1023,6 @@ static int exynos_tmu_probe(struct platform_device *pdev) goto err_clk; } - exynos_tmu_control(pdev, true); - - /* Allocate a structure to register with the exynos core thermal */ - sensor_conf = devm_kzalloc(&pdev->dev, - sizeof(struct thermal_sensor_conf), GFP_KERNEL); - if (!sensor_conf) { - ret = -ENOMEM; - goto err_clk; - } - sprintf(sensor_conf->name, "therm_zone%d", data->id); - sensor_conf->read_temperature = (int (*)(void *))exynos_tmu_read; - sensor_conf->write_emul_temp = - (int (*)(void *, unsigned long))exynos_tmu_set_emulation; - sensor_conf->driver_data = data; - sensor_conf->trip_data.trip_count = pdata->trigger_enable[0] + - pdata->trigger_enable[1] + pdata->trigger_enable[2]+ - pdata->trigger_enable[3]; - - for (i = 0; i < sensor_conf->trip_data.trip_count; i++) { - sensor_conf->trip_data.trip_val[i] = - pdata->threshold + pdata->trigger_levels[i]; - sensor_conf->trip_data.trip_type[i] = - pdata->trigger_type[i]; - } - - sensor_conf->trip_data.trigger_falling = pdata->threshold_falling; - - sensor_conf->dev = &pdev->dev; - /* Register the sensor with thermal management interface */ - ret = exynos_register_thermal(sensor_conf); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, - "Failed to register thermal interface: %d\n", - ret); - goto err_clk; - } - data->reg_conf = sensor_conf; - ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq, IRQF_TRIGGER_RISING | IRQF_SHARED, dev_name(&pdev->dev), data); if (ret) { @@ -935,21 +1030,25 @@ static int exynos_tmu_probe(struct platform_device *pdev) goto err_clk; } + exynos_tmu_control(pdev, true); return 0; err_clk: clk_unprepare(data->clk); err_clk_sec: if (!IS_ERR(data->clk_sec)) clk_unprepare(data->clk_sec); +err_sensor: + thermal_zone_of_sensor_unregister(&pdev->dev, data->tzd); + return ret; } static int exynos_tmu_remove(struct platform_device *pdev) { struct exynos_tmu_data *data = platform_get_drvdata(pdev); + struct thermal_zone_device *tzd = data->tzd; - exynos_unregister_thermal(data->reg_conf); - + thermal_zone_of_sensor_unregister(&pdev->dev, tzd); exynos_tmu_control(pdev, false); clk_unprepare(data->clk); diff --git a/drivers/thermal/samsung/exynos_tmu.h b/drivers/thermal/samsung/exynos_tmu.h index 627dec92ec1bfb..9f9b1b810269c5 100644 --- a/drivers/thermal/samsung/exynos_tmu.h +++ b/drivers/thermal/samsung/exynos_tmu.h @@ -23,8 +23,7 @@ #ifndef _EXYNOS_TMU_H #define _EXYNOS_TMU_H #include - -#include "exynos_thermal_common.h" +#include enum soc_type { SOC_ARCH_EXYNOS3250 = 1, @@ -36,38 +35,9 @@ enum soc_type { SOC_ARCH_EXYNOS5420_TRIMINFO, SOC_ARCH_EXYNOS5440, }; -#include /** * struct exynos_tmu_platform_data - * @threshold: basic temperature for generating interrupt - * 25 <= threshold <= 125 [unit: degree Celsius] - * @threshold_falling: differntial value for setting threshold - * of temperature falling interrupt. - * @trigger_levels: array for each interrupt levels - * [unit: degree Celsius] - * 0: temperature for trigger_level0 interrupt - * condition for trigger_level0 interrupt: - * current temperature > threshold + trigger_levels[0] - * 1: temperature for trigger_level1 interrupt - * condition for trigger_level1 interrupt: - * current temperature > threshold + trigger_levels[1] - * 2: temperature for trigger_level2 interrupt - * condition for trigger_level2 interrupt: - * current temperature > threshold + trigger_levels[2] - * 3: temperature for trigger_level3 interrupt - * condition for trigger_level3 interrupt: - * current temperature > threshold + trigger_levels[3] - * @trigger_type: defines the type of trigger. Possible values are, - * THROTTLE_ACTIVE trigger type - * THROTTLE_PASSIVE trigger type - * SW_TRIP trigger type - * HW_TRIP - * @trigger_enable[]: array to denote which trigger levels are enabled. - * 1 = enable trigger_level[] interrupt, - * 0 = disable trigger_level[] interrupt - * @max_trigger_level: max trigger level supported by the TMU - * @non_hw_trigger_levels: number of defined non-hardware trigger levels * @gain: gain of amplifier in the positive-TC generator block * 0 < gain <= 15 * @reference_voltage: reference voltage of amplifier @@ -79,21 +49,12 @@ enum soc_type { * @efuse_value: platform defined fuse value * @min_efuse_value: minimum valid trimming data * @max_efuse_value: maximum valid trimming data - * @first_point_trim: temp value of the first point trimming - * @second_point_trim: temp value of the second point trimming * @default_temp_offset: default temperature offset in case of no trimming * @cal_type: calibration type for temperature * * This structure is required for configuration of exynos_tmu driver. */ struct exynos_tmu_platform_data { - u8 threshold; - u8 threshold_falling; - u8 trigger_levels[MAX_TRIP_COUNT]; - enum trigger_type trigger_type[MAX_TRIP_COUNT]; - bool trigger_enable[MAX_TRIP_COUNT]; - u8 max_trigger_level; - u8 non_hw_trigger_levels; u8 gain; u8 reference_voltage; u8 noise_cancel_mode; @@ -110,24 +71,4 @@ struct exynos_tmu_platform_data { u32 cal_mode; }; -/** - * struct exynos_tmu_init_data - * @tmu_count: number of TMU instances. - * @tmu_data: platform data of all TMU instances. - * This structure is required to store data for multi-instance exynos tmu - * driver. - */ -struct exynos_tmu_init_data { - int tmu_count; - struct exynos_tmu_platform_data tmu_data[]; -}; - -extern struct exynos_tmu_init_data const exynos3250_default_tmu_data; -extern struct exynos_tmu_init_data const exynos4210_default_tmu_data; -extern struct exynos_tmu_init_data const exynos4412_default_tmu_data; -extern struct exynos_tmu_init_data const exynos5250_default_tmu_data; -extern struct exynos_tmu_init_data const exynos5260_default_tmu_data; -extern struct exynos_tmu_init_data const exynos5420_default_tmu_data; -extern struct exynos_tmu_init_data const exynos5440_default_tmu_data; - #endif /* _EXYNOS_TMU_H */ From 8d339b07a264a69eec1b6856fa6ce992d2146b3a Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 23 Jan 2015 13:10:09 +0100 Subject: [PATCH 641/788] thermal: exynos: Remove exynos_thermal_common.[c|h] files After defining all necessary Exynos data in the device tree and heavy reusage of the of-thermal.c those files can be removed. Signed-off-by: Lukasz Majewski --- .../thermal/samsung/exynos_thermal_common.c | 445 ------------------ .../thermal/samsung/exynos_thermal_common.h | 106 ----- 2 files changed, 551 deletions(-) delete mode 100644 drivers/thermal/samsung/exynos_thermal_common.c delete mode 100644 drivers/thermal/samsung/exynos_thermal_common.h diff --git a/drivers/thermal/samsung/exynos_thermal_common.c b/drivers/thermal/samsung/exynos_thermal_common.c deleted file mode 100644 index 00aa68862a522e..00000000000000 --- a/drivers/thermal/samsung/exynos_thermal_common.c +++ /dev/null @@ -1,445 +0,0 @@ -/* - * exynos_thermal_common.c - Samsung EXYNOS common thermal file - * - * Copyright (C) 2013 Samsung Electronics - * Amit Daniel Kachhap - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include -#include -#include -#include - -#include "exynos_thermal_common.h" - -struct exynos_thermal_zone { - enum thermal_device_mode mode; - struct thermal_zone_device *therm_dev; - struct thermal_cooling_device *cool_dev[MAX_COOLING_DEVICE]; - unsigned int cool_dev_size; - struct platform_device *exynos4_dev; - struct thermal_sensor_conf *sensor_conf; - bool bind; -}; - -/* Get mode callback functions for thermal zone */ -static int exynos_get_mode(struct thermal_zone_device *thermal, - enum thermal_device_mode *mode) -{ - struct exynos_thermal_zone *th_zone = thermal->devdata; - if (th_zone) - *mode = th_zone->mode; - return 0; -} - -/* Set mode callback functions for thermal zone */ -static int exynos_set_mode(struct thermal_zone_device *thermal, - enum thermal_device_mode mode) -{ - struct exynos_thermal_zone *th_zone = thermal->devdata; - if (!th_zone) { - dev_err(&thermal->device, - "thermal zone not registered\n"); - return 0; - } - - mutex_lock(&thermal->lock); - - if (mode == THERMAL_DEVICE_ENABLED && - !th_zone->sensor_conf->trip_data.trigger_falling) - thermal->polling_delay = IDLE_INTERVAL; - else - thermal->polling_delay = 0; - - mutex_unlock(&thermal->lock); - - th_zone->mode = mode; - thermal_zone_device_update(thermal); - dev_dbg(th_zone->sensor_conf->dev, - "thermal polling set for duration=%d msec\n", - thermal->polling_delay); - return 0; -} - - -/* Get trip type callback functions for thermal zone */ -static int exynos_get_trip_type(struct thermal_zone_device *thermal, int trip, - enum thermal_trip_type *type) -{ - struct exynos_thermal_zone *th_zone = thermal->devdata; - int max_trip = th_zone->sensor_conf->trip_data.trip_count; - int trip_type; - - if (trip < 0 || trip >= max_trip) - return -EINVAL; - - trip_type = th_zone->sensor_conf->trip_data.trip_type[trip]; - - if (trip_type == SW_TRIP) - *type = THERMAL_TRIP_CRITICAL; - else if (trip_type == THROTTLE_ACTIVE) - *type = THERMAL_TRIP_ACTIVE; - else if (trip_type == THROTTLE_PASSIVE) - *type = THERMAL_TRIP_PASSIVE; - else - return -EINVAL; - - return 0; -} - -/* Get trip temperature callback functions for thermal zone */ -static int exynos_get_trip_temp(struct thermal_zone_device *thermal, int trip, - unsigned long *temp) -{ - struct exynos_thermal_zone *th_zone = thermal->devdata; - int max_trip = th_zone->sensor_conf->trip_data.trip_count; - - if (trip < 0 || trip >= max_trip) - return -EINVAL; - - *temp = th_zone->sensor_conf->trip_data.trip_val[trip]; - /* convert the temperature into millicelsius */ - *temp = *temp * MCELSIUS; - - return 0; -} - -/* Get critical temperature callback functions for thermal zone */ -static int exynos_get_crit_temp(struct thermal_zone_device *thermal, - unsigned long *temp) -{ - struct exynos_thermal_zone *th_zone = thermal->devdata; - int max_trip = th_zone->sensor_conf->trip_data.trip_count; - /* Get the temp of highest trip*/ - return exynos_get_trip_temp(thermal, max_trip - 1, temp); -} - -/* Bind callback functions for thermal zone */ -static int exynos_bind(struct thermal_zone_device *thermal, - struct thermal_cooling_device *cdev) -{ - struct exynos_thermal_zone *th_zone = thermal->devdata; - struct thermal_sensor_conf *data = th_zone->sensor_conf; - struct device_node *child, *gchild, *np; - struct of_phandle_args cooling_spec; - unsigned long max, state = 0; - int ret = 0, i = 0; - - /* - * Below code is necessary to skip binding when cpufreq's - * frequency table is not yet initialized. - */ - cdev->ops->get_max_state(cdev, &state); - if (!state && !th_zone->cool_dev_size) { - th_zone->cool_dev_size = 1; - th_zone->cool_dev[0] = cdev; - th_zone->bind = false; - return 0; - } - - np = of_find_node_by_path("/thermal-zones/cpu-thermal"); - if (!np) { - pr_err("failed to find thmerla-zones/cpu-thermal node\n"); - return -ENOENT; - } - - child = of_get_child_by_name(np, "cooling-maps"); - - for_each_child_of_node(child, gchild) { - ret = of_parse_phandle_with_args(gchild, "cooling-device", - "#cooling-cells", - 0, &cooling_spec); - if (ret < 0) { - pr_err("missing cooling_device property\n"); - goto end; - } - - if (cooling_spec.args_count < 2) { - ret = -EINVAL; - goto end; - } - - max = cooling_spec.args[0]; - if (thermal_zone_bind_cooling_device(thermal, i, cdev, - max, 0)) { - dev_err(data->dev, - "thermal error unbinding cdev inst=%d\n", i); - - ret = -EINVAL; - goto end; - } - i++; - } - th_zone->bind = true; -end: - of_node_put(child); - of_node_put(np); - - return ret; -} - -/* Unbind callback functions for thermal zone */ -static int exynos_unbind(struct thermal_zone_device *thermal, - struct thermal_cooling_device *cdev) -{ - int ret = 0, i; - struct exynos_thermal_zone *th_zone = thermal->devdata; - struct thermal_sensor_conf *data = th_zone->sensor_conf; - struct device_node *child, *gchild, *np; - - if (th_zone->bind == false || !th_zone->cool_dev_size) - return 0; - - /* find the cooling device registered*/ - for (i = 0; i < th_zone->cool_dev_size; i++) - if (cdev == th_zone->cool_dev[i]) - break; - - /* No matching cooling device */ - if (i == th_zone->cool_dev_size) - return 0; - - np = of_find_node_by_path("/thermal-zones/cpu-thermal"); - if (!np) { - pr_err("failed to find thmerla-zones/cpu-thermal node\n"); - return -ENOENT; - } - - child = of_get_child_by_name(np, "cooling-maps"); - - i = 0; - for_each_child_of_node(child, gchild) { - if (thermal_zone_unbind_cooling_device(thermal, i, - cdev)) { - dev_err(data->dev, - "error unbinding cdev inst=%d\n", i); - ret = -EINVAL; - goto end; - } - i++; - } - th_zone->bind = false; -end: - of_node_put(child); - of_node_put(np); - - return ret; -} - -/* Get temperature callback functions for thermal zone */ -static int exynos_get_temp(struct thermal_zone_device *thermal, - unsigned long *temp) -{ - struct exynos_thermal_zone *th_zone = thermal->devdata; - void *data; - - if (!th_zone->sensor_conf) { - dev_err(&thermal->device, - "Temperature sensor not initialised\n"); - return -EINVAL; - } - data = th_zone->sensor_conf->driver_data; - *temp = th_zone->sensor_conf->read_temperature(data); - /* convert the temperature into millicelsius */ - *temp = *temp * MCELSIUS; - return 0; -} - -/* Get temperature callback functions for thermal zone */ -static int exynos_set_emul_temp(struct thermal_zone_device *thermal, - unsigned long temp) -{ - void *data; - int ret = -EINVAL; - struct exynos_thermal_zone *th_zone = thermal->devdata; - - if (!th_zone->sensor_conf) { - dev_err(&thermal->device, - "Temperature sensor not initialised\n"); - return -EINVAL; - } - data = th_zone->sensor_conf->driver_data; - if (th_zone->sensor_conf->write_emul_temp) - ret = th_zone->sensor_conf->write_emul_temp(data, temp); - return ret; -} - -/* Get the temperature trend */ -static int exynos_get_trend(struct thermal_zone_device *thermal, - int trip, enum thermal_trend *trend) -{ - int ret; - unsigned long trip_temp; - - ret = exynos_get_trip_temp(thermal, trip, &trip_temp); - if (ret < 0) - return ret; - - if (thermal->temperature >= trip_temp) - *trend = THERMAL_TREND_RAISE_FULL; - else - *trend = THERMAL_TREND_DROP_FULL; - - return 0; -} -/* Operation callback functions for thermal zone */ -static struct thermal_zone_device_ops exynos_dev_ops = { - .bind = exynos_bind, - .unbind = exynos_unbind, - .get_temp = exynos_get_temp, - .set_emul_temp = exynos_set_emul_temp, - .get_trend = exynos_get_trend, - .get_mode = exynos_get_mode, - .set_mode = exynos_set_mode, - .get_trip_type = exynos_get_trip_type, - .get_trip_temp = exynos_get_trip_temp, - .get_crit_temp = exynos_get_crit_temp, -}; - -/* - * This function may be called from interrupt based temperature sensor - * when threshold is changed. - */ -void exynos_report_trigger(struct thermal_sensor_conf *conf) -{ - unsigned int i; - char data[10]; - char *envp[] = { data, NULL }; - struct exynos_thermal_zone *th_zone; - - if (!conf || !conf->pzone_data) { - pr_err("Invalid temperature sensor configuration data\n"); - return; - } - - th_zone = conf->pzone_data; - - if (th_zone->bind == false) { - for (i = 0; i < th_zone->cool_dev_size; i++) { - if (!th_zone->cool_dev[i]) - continue; - exynos_bind(th_zone->therm_dev, - th_zone->cool_dev[i]); - } - } - - thermal_zone_device_update(th_zone->therm_dev); - - mutex_lock(&th_zone->therm_dev->lock); - /* Find the level for which trip happened */ - for (i = 0; i < th_zone->sensor_conf->trip_data.trip_count; i++) { - if (th_zone->therm_dev->last_temperature < - th_zone->sensor_conf->trip_data.trip_val[i] * MCELSIUS) - break; - } - - if (th_zone->mode == THERMAL_DEVICE_ENABLED && - !th_zone->sensor_conf->trip_data.trigger_falling) { - if (i > 0) - th_zone->therm_dev->polling_delay = ACTIVE_INTERVAL; - else - th_zone->therm_dev->polling_delay = IDLE_INTERVAL; - } - - snprintf(data, sizeof(data), "%u", i); - kobject_uevent_env(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE, envp); - mutex_unlock(&th_zone->therm_dev->lock); -} - -/* Register with the in-kernel thermal management */ -int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf) -{ - int ret; - struct exynos_thermal_zone *th_zone; - - if (!sensor_conf || !sensor_conf->read_temperature) { - pr_err("Temperature sensor not initialised\n"); - return -EINVAL; - } - - th_zone = devm_kzalloc(sensor_conf->dev, - sizeof(struct exynos_thermal_zone), GFP_KERNEL); - if (!th_zone) - return -ENOMEM; - - th_zone->sensor_conf = sensor_conf; - /* - * TODO: 1) Handle multiple cooling devices in a thermal zone - * 2) Add a flag/name in cooling info to map to specific - * sensor - */ - if (sensor_conf->cooling_data.freq_clip_count > 0) { - th_zone->cool_dev[th_zone->cool_dev_size] = - cpufreq_cooling_register(cpu_present_mask); - if (IS_ERR(th_zone->cool_dev[th_zone->cool_dev_size])) { - ret = PTR_ERR(th_zone->cool_dev[th_zone->cool_dev_size]); - if (ret != -EPROBE_DEFER) - dev_err(sensor_conf->dev, - "Failed to register cpufreq cooling device: %d\n", - ret); - goto err_unregister; - } - th_zone->cool_dev_size++; - } - - th_zone->therm_dev = thermal_zone_device_register( - sensor_conf->name, sensor_conf->trip_data.trip_count, - 0, th_zone, &exynos_dev_ops, NULL, 0, - sensor_conf->trip_data.trigger_falling ? 0 : - IDLE_INTERVAL); - - if (IS_ERR(th_zone->therm_dev)) { - dev_err(sensor_conf->dev, - "Failed to register thermal zone device\n"); - ret = PTR_ERR(th_zone->therm_dev); - goto err_unregister; - } - th_zone->mode = THERMAL_DEVICE_ENABLED; - sensor_conf->pzone_data = th_zone; - - dev_info(sensor_conf->dev, - "Exynos: Thermal zone(%s) registered\n", sensor_conf->name); - - return 0; - -err_unregister: - exynos_unregister_thermal(sensor_conf); - return ret; -} - -/* Un-Register with the in-kernel thermal management */ -void exynos_unregister_thermal(struct thermal_sensor_conf *sensor_conf) -{ - int i; - struct exynos_thermal_zone *th_zone; - - if (!sensor_conf || !sensor_conf->pzone_data) { - pr_err("Invalid temperature sensor configuration data\n"); - return; - } - - th_zone = sensor_conf->pzone_data; - - thermal_zone_device_unregister(th_zone->therm_dev); - - for (i = 0; i < th_zone->cool_dev_size; ++i) - cpufreq_cooling_unregister(th_zone->cool_dev[i]); - - dev_info(sensor_conf->dev, - "Exynos: Kernel Thermal management unregistered\n"); -} diff --git a/drivers/thermal/samsung/exynos_thermal_common.h b/drivers/thermal/samsung/exynos_thermal_common.h deleted file mode 100644 index cd4471925cddba..00000000000000 --- a/drivers/thermal/samsung/exynos_thermal_common.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * exynos_thermal_common.h - Samsung EXYNOS common header file - * - * Copyright (C) 2013 Samsung Electronics - * Amit Daniel Kachhap - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef _EXYNOS_THERMAL_COMMON_H -#define _EXYNOS_THERMAL_COMMON_H - -/* In-kernel thermal framework related macros & definations */ -#define SENSOR_NAME_LEN 16 -#define MAX_TRIP_COUNT 8 -#define MAX_COOLING_DEVICE 4 - -#define ACTIVE_INTERVAL 500 -#define IDLE_INTERVAL 10000 -#define MCELSIUS 1000 - -/* CPU Zone information */ -#define PANIC_ZONE 4 -#define WARN_ZONE 3 -#define MONITOR_ZONE 2 -#define SAFE_ZONE 1 - -#define GET_ZONE(trip) (trip + 2) -#define GET_TRIP(zone) (zone - 2) - -enum trigger_type { - THROTTLE_ACTIVE = 1, - THROTTLE_PASSIVE, - SW_TRIP, - HW_TRIP, -}; - -/** - * struct freq_clip_table - * @freq_clip_max: maximum frequency allowed for this cooling state. - * @temp_level: Temperature level at which the temperature clipping will - * happen. - * @mask_val: cpumask of the allowed cpu's where the clipping will take place. - * - * This structure is required to be filled and passed to the - * cpufreq_cooling_unregister function. - */ -struct freq_clip_table { - unsigned int freq_clip_max; - unsigned int temp_level; - const struct cpumask *mask_val; -}; - -struct thermal_trip_point_conf { - int trip_val[MAX_TRIP_COUNT]; - int trip_type[MAX_TRIP_COUNT]; - int trip_count; - unsigned char trigger_falling; -}; - -struct thermal_cooling_conf { - struct freq_clip_table freq_data[MAX_TRIP_COUNT]; - int freq_clip_count; -}; - -struct thermal_sensor_conf { - char name[SENSOR_NAME_LEN]; - int (*read_temperature)(void *data); - int (*write_emul_temp)(void *drv_data, unsigned long temp); - struct thermal_trip_point_conf trip_data; - struct thermal_cooling_conf cooling_data; - void *driver_data; - void *pzone_data; - struct device *dev; -}; - -/*Functions used exynos based thermal sensor driver*/ -#ifdef CONFIG_EXYNOS_THERMAL_CORE -void exynos_unregister_thermal(struct thermal_sensor_conf *sensor_conf); -int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf); -void exynos_report_trigger(struct thermal_sensor_conf *sensor_conf); -#else -static inline void -exynos_unregister_thermal(struct thermal_sensor_conf *sensor_conf) { return; } - -static inline int -exynos_register_thermal(struct thermal_sensor_conf *sensor_conf) { return 0; } - -static inline void -exynos_report_trigger(struct thermal_sensor_conf *sensor_conf) { return; } - -#endif /* CONFIG_EXYNOS_THERMAL_CORE */ -#endif /* _EXYNOS_THERMAL_COMMON_H */ From 76a67707cead0eb127c37696c425f459ff1ac083 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 23 Jan 2015 13:10:10 +0100 Subject: [PATCH 642/788] thermal: exynos: Remove exynos_tmu_data.c file Data already present in the exynos_tmu_data.c file has been moved to the appropriate device tree files. Signed-off-by: Lukasz Majewski --- drivers/thermal/samsung/exynos_tmu_data.c | 228 ---------------------- 1 file changed, 228 deletions(-) delete mode 100644 drivers/thermal/samsung/exynos_tmu_data.c diff --git a/drivers/thermal/samsung/exynos_tmu_data.c b/drivers/thermal/samsung/exynos_tmu_data.c deleted file mode 100644 index a993f3d33f9b6a..00000000000000 --- a/drivers/thermal/samsung/exynos_tmu_data.c +++ /dev/null @@ -1,228 +0,0 @@ -/* - * exynos_tmu_data.c - Samsung EXYNOS tmu data file - * - * Copyright (C) 2013 Samsung Electronics - * Amit Daniel Kachhap - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "exynos_thermal_common.h" -#include "exynos_tmu.h" - -struct exynos_tmu_init_data const exynos4210_default_tmu_data = { - .tmu_data = { - { - .threshold = 80, - .trigger_levels[0] = 5, - .trigger_levels[1] = 20, - .trigger_levels[2] = 30, - .trigger_enable[0] = true, - .trigger_enable[1] = true, - .trigger_enable[2] = true, - .trigger_enable[3] = false, - .trigger_type[0] = THROTTLE_ACTIVE, - .trigger_type[1] = THROTTLE_ACTIVE, - .trigger_type[2] = SW_TRIP, - .max_trigger_level = 4, - .non_hw_trigger_levels = 3, - .gain = 15, - .reference_voltage = 7, - .cal_type = TYPE_ONE_POINT_TRIMMING, - .min_efuse_value = 40, - .max_efuse_value = 100, - .first_point_trim = 25, - .second_point_trim = 85, - .default_temp_offset = 50, - .type = SOC_ARCH_EXYNOS4210, - }, - }, - .tmu_count = 1, -}; - -#define EXYNOS3250_TMU_DATA \ - .threshold_falling = 10, \ - .trigger_levels[0] = 70, \ - .trigger_levels[1] = 95, \ - .trigger_levels[2] = 110, \ - .trigger_levels[3] = 120, \ - .trigger_enable[0] = true, \ - .trigger_enable[1] = true, \ - .trigger_enable[2] = true, \ - .trigger_enable[3] = false, \ - .trigger_type[0] = THROTTLE_ACTIVE, \ - .trigger_type[1] = THROTTLE_ACTIVE, \ - .trigger_type[2] = SW_TRIP, \ - .trigger_type[3] = HW_TRIP, \ - .max_trigger_level = 4, \ - .non_hw_trigger_levels = 3, \ - .gain = 8, \ - .reference_voltage = 16, \ - .noise_cancel_mode = 4, \ - .cal_type = TYPE_TWO_POINT_TRIMMING, \ - .efuse_value = 55, \ - .min_efuse_value = 40, \ - .max_efuse_value = 100, \ - .first_point_trim = 25, \ - .second_point_trim = 85, \ - .default_temp_offset = 50 - -struct exynos_tmu_init_data const exynos3250_default_tmu_data = { - .tmu_data = { - { - EXYNOS3250_TMU_DATA, - .type = SOC_ARCH_EXYNOS3250, - }, - }, - .tmu_count = 1, -}; - -#define EXYNOS4412_TMU_DATA \ - .threshold_falling = 10, \ - .trigger_levels[0] = 70, \ - .trigger_levels[1] = 95, \ - .trigger_levels[2] = 110, \ - .trigger_levels[3] = 120, \ - .trigger_enable[0] = true, \ - .trigger_enable[1] = true, \ - .trigger_enable[2] = true, \ - .trigger_enable[3] = false, \ - .trigger_type[0] = THROTTLE_ACTIVE, \ - .trigger_type[1] = THROTTLE_ACTIVE, \ - .trigger_type[2] = SW_TRIP, \ - .trigger_type[3] = HW_TRIP, \ - .max_trigger_level = 4, \ - .non_hw_trigger_levels = 3, \ - .gain = 8, \ - .reference_voltage = 16, \ - .noise_cancel_mode = 4, \ - .cal_type = TYPE_ONE_POINT_TRIMMING, \ - .efuse_value = 55, \ - .min_efuse_value = 40, \ - .max_efuse_value = 100, \ - .first_point_trim = 25, \ - .second_point_trim = 85, \ - .default_temp_offset = 50 - -struct exynos_tmu_init_data const exynos4412_default_tmu_data = { - .tmu_data = { - { - EXYNOS4412_TMU_DATA, - .type = SOC_ARCH_EXYNOS4412, - }, - }, - .tmu_count = 1, -}; - -struct exynos_tmu_init_data const exynos5250_default_tmu_data = { - .tmu_data = { - { - EXYNOS4412_TMU_DATA, - .type = SOC_ARCH_EXYNOS5250, - }, - }, - .tmu_count = 1, -}; - -#define __EXYNOS5260_TMU_DATA \ - .threshold_falling = 10, \ - .trigger_levels[0] = 85, \ - .trigger_levels[1] = 103, \ - .trigger_levels[2] = 110, \ - .trigger_levels[3] = 120, \ - .trigger_enable[0] = true, \ - .trigger_enable[1] = true, \ - .trigger_enable[2] = true, \ - .trigger_enable[3] = false, \ - .trigger_type[0] = THROTTLE_ACTIVE, \ - .trigger_type[1] = THROTTLE_ACTIVE, \ - .trigger_type[2] = SW_TRIP, \ - .trigger_type[3] = HW_TRIP, \ - .max_trigger_level = 4, \ - .non_hw_trigger_levels = 3, \ - .gain = 8, \ - .reference_voltage = 16, \ - .noise_cancel_mode = 4, \ - .cal_type = TYPE_ONE_POINT_TRIMMING, \ - .efuse_value = 55, \ - .min_efuse_value = 40, \ - .max_efuse_value = 100, \ - .first_point_trim = 25, \ - .second_point_trim = 85, \ - .default_temp_offset = 50, - -#define EXYNOS5260_TMU_DATA \ - __EXYNOS5260_TMU_DATA \ - .type = SOC_ARCH_EXYNOS5260 - -struct exynos_tmu_init_data const exynos5260_default_tmu_data = { - .tmu_data = { - { EXYNOS5260_TMU_DATA }, - { EXYNOS5260_TMU_DATA }, - { EXYNOS5260_TMU_DATA }, - { EXYNOS5260_TMU_DATA }, - { EXYNOS5260_TMU_DATA }, - }, - .tmu_count = 5, -}; - -#define EXYNOS5420_TMU_DATA \ - __EXYNOS5260_TMU_DATA \ - .type = SOC_ARCH_EXYNOS5420 - -#define EXYNOS5420_TMU_DATA_SHARED \ - __EXYNOS5260_TMU_DATA \ - .type = SOC_ARCH_EXYNOS5420_TRIMINFO - -struct exynos_tmu_init_data const exynos5420_default_tmu_data = { - .tmu_data = { - { EXYNOS5420_TMU_DATA }, - { EXYNOS5420_TMU_DATA }, - { EXYNOS5420_TMU_DATA_SHARED }, - { EXYNOS5420_TMU_DATA_SHARED }, - { EXYNOS5420_TMU_DATA_SHARED }, - }, - .tmu_count = 5, -}; - -#define EXYNOS5440_TMU_DATA \ - .trigger_levels[0] = 100, \ - .trigger_levels[4] = 105, \ - .trigger_enable[0] = 1, \ - .trigger_type[0] = SW_TRIP, \ - .trigger_type[4] = HW_TRIP, \ - .max_trigger_level = 5, \ - .non_hw_trigger_levels = 1, \ - .gain = 5, \ - .reference_voltage = 16, \ - .noise_cancel_mode = 4, \ - .cal_type = TYPE_ONE_POINT_TRIMMING, \ - .efuse_value = 0x5b2d, \ - .min_efuse_value = 16, \ - .max_efuse_value = 76, \ - .first_point_trim = 25, \ - .second_point_trim = 70, \ - .default_temp_offset = 25, \ - .type = SOC_ARCH_EXYNOS5440 - -struct exynos_tmu_init_data const exynos5440_default_tmu_data = { - .tmu_data = { - { EXYNOS5440_TMU_DATA } , - { EXYNOS5440_TMU_DATA } , - { EXYNOS5440_TMU_DATA } , - }, - .tmu_count = 3, -}; From aaa91f06be8bee33bedd502a12219e8963830051 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Thu, 15 Jan 2015 15:28:40 +0100 Subject: [PATCH 643/788] patchset: [PATCH v6 00/18] thermal: exynos: Thermal code rework to use device tree --- PATCHES | 1 + 1 file changed, 1 insertion(+) diff --git a/PATCHES b/PATCHES index b0d680f92c3083..b650bc5f50a75d 100644 --- a/PATCHES +++ b/PATCHES @@ -2,3 +2,4 @@ [PATCH v2 0/6] Enable HDMI support on Exynos platforms [PATCH V3 00/15] ASoC: samsung: Add clk provider for I2S internal clocks [PATCHv3 0/8] devfreq: Add generic exynos memory-bus frequency driver +[PATCH v6 00/18] thermal: exynos: Thermal code rework to use device tree From 67a718910396b7a3556280b6128966b23d81f1c5 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Mon, 19 Jan 2015 12:44:03 +0100 Subject: [PATCH 644/788] thermal: exynos: Reorder exynos_map_dt_data() function The exynos_map_dt_data() function must be called before thermal_zone_of_sensor_register(), and hence provide tmu_read() function, before it is needed. This change is driven by adding support for enabling thermal_zoneX when it is properly initialized. One can read the mode of operation at /sys/class/thermal/thermal_zone0/mode Such functionality was missing in the of-thermal.c code. Reported-by: Abhilash Kesavan Signed-off-by: Lukasz Majewski --- drivers/thermal/samsung/exynos_tmu.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c index 864eec8f5164f0..852e622d128f32 100644 --- a/drivers/thermal/samsung/exynos_tmu.c +++ b/drivers/thermal/samsung/exynos_tmu.c @@ -975,15 +975,16 @@ static int exynos_tmu_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); mutex_init(&data->lock); + ret = exynos_map_dt_data(pdev); + if (ret) + goto err_sensor; + data->tzd = thermal_zone_of_sensor_register(&pdev->dev, 0, data, &exynos_sensor_ops); if (IS_ERR(data->tzd)) { pr_err("thermal: tz: %p ERROR\n", data->tzd); return PTR_ERR(data->tzd); } - ret = exynos_map_dt_data(pdev); - if (ret) - goto err_sensor; pdata = data->pdata; From 5626c7831cf6c560cfad24e43b9d1f7c0e14c560 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Mon, 19 Jan 2015 12:44:04 +0100 Subject: [PATCH 645/788] thermal: of: Enable thermal_zoneX when sensor is correctly added Up till now the thermal_zone mode was by default "disabled". With this patch the default behavior was changed to "enable". One can read the mode at: /sys/class/thermal/thermal_zone0/mode Reported-by: Abhilash Kesavan Signed-off-by: Lukasz Majewski Tested-by: Javi Merino --- drivers/thermal/of-thermal.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c index d717f3dab6f141..668fb1bdea9eff 100644 --- a/drivers/thermal/of-thermal.c +++ b/drivers/thermal/of-thermal.c @@ -497,6 +497,9 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data, if (sensor_specs.np == sensor_np && id == sensor_id) { tzd = thermal_zone_of_add_sensor(child, sensor_np, data, ops); + if (!IS_ERR(tzd)) + tzd->ops->set_mode(tzd, THERMAL_DEVICE_ENABLED); + of_node_put(sensor_specs.np); of_node_put(child); goto exit; From f6c4f5a052fb99e6109765cfadd748e0f11bb673 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Wed, 28 Jan 2015 16:25:22 +0100 Subject: [PATCH 646/788] thermal: exynos: Correct sanity check at exynos_report_trigger() function Up till now, by mistake, wrong variable was tested against being NULL. Since exynos_report_trigger() is always called with valid p pointer, it is only necessary to check if a valid thermal zone device is passed. Reported-by: Dan Carpenter Signed-off-by: Lukasz Majewski --- drivers/thermal/samsung/exynos_tmu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c index 852e622d128f32..178de03a1842fb 100644 --- a/drivers/thermal/samsung/exynos_tmu.c +++ b/drivers/thermal/samsung/exynos_tmu.c @@ -172,8 +172,8 @@ static void exynos_report_trigger(struct exynos_tmu_data *p) unsigned long temp; unsigned int i; - if (!p) { - pr_err("Wrong temperature configuration data\n"); + if (!tz) { + pr_err("No thermal zone device defined\n"); return; } From 0c116050d2ab631e090ced2c06a434f1664d6f27 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Wed, 28 Jan 2015 16:28:38 +0100 Subject: [PATCH 647/788] exynos: config: Remove CONFIG_EXYNOS_THERMAL_CORE define (exynos_defconfig) After Exynos TMU rework to use device tree for configuration this flag can be removed. It is not used anymore. Signed-off-by: Lukasz Majewski Acked-by: Eduardo Valentin --- arch/arm/configs/exynos_defconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm/configs/exynos_defconfig b/arch/arm/configs/exynos_defconfig index 3d0c5d65c74193..3691a689e79c1f 100644 --- a/arch/arm/configs/exynos_defconfig +++ b/arch/arm/configs/exynos_defconfig @@ -88,7 +88,6 @@ CONFIG_HWMON=y CONFIG_SENSORS_LM90=y CONFIG_THERMAL=y CONFIG_EXYNOS_THERMAL=y -CONFIG_EXYNOS_THERMAL_CORE=y CONFIG_WATCHDOG=y CONFIG_S3C2410_WATCHDOG=y CONFIG_MFD_CROS_EC=y From f13fc5ae306b53dad4ef96731ade8518ea1c42ec Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Wed, 28 Jan 2015 16:28:39 +0100 Subject: [PATCH 648/788] exynos: config: Enable thermal emulation for Exynos TMU (exynos_defconfig) Enabling thermal emulation on Exynos SoCs. New sysfs attribute - emul_temp is created. Signed-off-by: Lukasz Majewski Acked-by: Eduardo Valentin --- arch/arm/configs/exynos_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/exynos_defconfig b/arch/arm/configs/exynos_defconfig index 3691a689e79c1f..376dbf43a6b940 100644 --- a/arch/arm/configs/exynos_defconfig +++ b/arch/arm/configs/exynos_defconfig @@ -88,6 +88,7 @@ CONFIG_HWMON=y CONFIG_SENSORS_LM90=y CONFIG_THERMAL=y CONFIG_EXYNOS_THERMAL=y +CONFIG_THERMAL_EMULATION=y CONFIG_WATCHDOG=y CONFIG_S3C2410_WATCHDOG=y CONFIG_MFD_CROS_EC=y From 9814c034d6f95d4af65318870718f6baa02dd56b Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Wed, 28 Jan 2015 16:28:40 +0100 Subject: [PATCH 649/788] exynos: config: Enable support for cpufreq on Exynos SoCs (exynos_defconfig) This commit enables the cpufreq subsystem. Moreover, support for using CPU as a cooling device is provided. Signed-off-by: Lukasz Majewski Acked-by: Eduardo Valentin --- arch/arm/configs/exynos_defconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/configs/exynos_defconfig b/arch/arm/configs/exynos_defconfig index 376dbf43a6b940..d6be7e8ee4e5f2 100644 --- a/arch/arm/configs/exynos_defconfig +++ b/arch/arm/configs/exynos_defconfig @@ -86,6 +86,8 @@ CONFIG_BATTERY_SBS=y CONFIG_CHARGER_TPS65090=y CONFIG_HWMON=y CONFIG_SENSORS_LM90=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_THERMAL=y CONFIG_THERMAL=y CONFIG_EXYNOS_THERMAL=y CONFIG_THERMAL_EMULATION=y From c8d6fe545aff20965180b19e52b8556bd81adf40 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Mon, 26 Jan 2015 10:25:27 +0100 Subject: [PATCH 650/788] cpufreq: exynos: Use simple approach to asses if cpu cooling can be used Commit: e725d26c4857e5e41975b5e74e64ce6ab09a7121 provided possibility to use device tree to asses if cpu can be used as cooling device. Since the code was somewhat awkward, simpler approach has been proposed. Test HW: Exynos 4412 - Odroid U3. Suggested-by: Viresh Kumar Signed-off-by: Lukasz Majewski Acked-by: Viresh Kumar --- drivers/cpufreq/exynos-cpufreq.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c index 5e98c6b1f284b6..b640da04ad6b11 100644 --- a/drivers/cpufreq/exynos-cpufreq.c +++ b/drivers/cpufreq/exynos-cpufreq.c @@ -159,7 +159,7 @@ static struct cpufreq_driver exynos_driver = { static int exynos_cpufreq_probe(struct platform_device *pdev) { - struct device_node *cpus, *np; + struct device_node *cpu0, *np; int ret = -EINVAL; exynos_info = kzalloc(sizeof(*exynos_info), GFP_KERNEL); @@ -206,28 +206,19 @@ static int exynos_cpufreq_probe(struct platform_device *pdev) if (ret) goto err_cpufreq_reg; - cpus = of_find_node_by_path("/cpus"); - if (!cpus) { - pr_err("failed to find cpus node\n"); + cpu0 = of_get_cpu_node(0, NULL); + if (!cpu0) { + pr_err("failed to find cpu0 node\n"); return 0; } - np = of_get_next_child(cpus, NULL); - if (!np) { - pr_err("failed to find cpus child node\n"); - of_node_put(cpus); - return 0; - } - - if (of_find_property(np, "#cooling-cells", NULL)) { - cdev = of_cpufreq_cooling_register(np, + if (of_find_property(cpu0, "#cooling-cells", NULL)) { + cdev = of_cpufreq_cooling_register(cpu0, cpu_present_mask); if (IS_ERR(cdev)) pr_err("running cpufreq without cooling device: %ld\n", PTR_ERR(cdev)); } - of_node_put(np); - of_node_put(cpus); return 0; From c79eed0a68f086a011a088f249422751c0146280 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 23 Jan 2015 16:33:40 -0800 Subject: [PATCH 651/788] mmc: dw_mmc: exynos: remove incorrect __exit_p() dw_mci_pltfm_remove() is not (nor should it be) marked as __exit, so we should not be using __exit_p() wrapper with it. Signed-off-by: Dmitry Torokhov Reviewed-by: Doug Anderson Tested-by: Doug Anderson --- drivers/mmc/host/dw_mmc-exynos.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index 509365cb22c6ad..d0a3b9a9bef098 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -499,7 +499,7 @@ static const struct dev_pm_ops dw_mci_exynos_pmops = { static struct platform_driver dw_mci_exynos_pltfm_driver = { .probe = dw_mci_exynos_probe, - .remove = __exit_p(dw_mci_pltfm_remove), + .remove = dw_mci_pltfm_remove, .driver = { .name = "dwmmc_exynos", .of_match_table = dw_mci_exynos_match, From f3197ab981dd7c1e5994750c9757e80293f1ad50 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 23 Jan 2015 16:51:11 +0100 Subject: [PATCH 652/788] drm: exynos: detach from default dma-mapping domain on init This patch adds code, which detach sub-device nodes from default iommu domain if such has been configured. Signed-off-by: Marek Szyprowski --- drivers/gpu/drm/exynos/exynos_drm_iommu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/exynos/exynos_drm_iommu.c b/drivers/gpu/drm/exynos/exynos_drm_iommu.c index b32b291f88ff0b..323601a52a2509 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_iommu.c +++ b/drivers/gpu/drm/exynos/exynos_drm_iommu.c @@ -100,6 +100,9 @@ int drm_iommu_attach_device(struct drm_device *drm_dev, dma_set_max_seg_size(subdrv_dev, 0xffffffffu); + if (subdrv_dev->archdata.mapping) + arm_iommu_detach_device(subdrv_dev); + ret = arm_iommu_attach_device(subdrv_dev, dev->archdata.mapping); if (ret < 0) { DRM_DEBUG_KMS("failed iommu attach.\n"); From fcee34fbd355cfb0519aa06b89cdc266dcacd4a9 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 23 Jan 2015 16:51:12 +0100 Subject: [PATCH 653/788] arm: exynos: pm_domains: add support for devices registered before arch_initcall SYSMMU devices will be registered early before any other devices and before calling arch_initcall. To add them to respective power domains, additional scan of all platform devices is needed. Signed-off-by: Marek Szyprowski --- arch/arm/mach-exynos/pm_domains.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-exynos/pm_domains.c b/arch/arm/mach-exynos/pm_domains.c index 37266a8264372a..0e2bc366b5eb6d 100644 --- a/arch/arm/mach-exynos/pm_domains.c +++ b/arch/arm/mach-exynos/pm_domains.c @@ -105,6 +105,12 @@ static int exynos_pd_power_off(struct generic_pm_domain *domain) return exynos_pd_power(domain, false); } +static __init int exynos_pd_init_platform_dev(struct device *dev, void *data) +{ + dev_pm_domain_attach(dev, true); + return 0; +} + static __init int exynos4_pm_init_power_domain(void) { struct platform_device *pdev; @@ -189,6 +195,7 @@ static __init int exynos4_pm_init_power_domain(void) of_node_put(np); } - return 0; + return bus_for_each_dev(&platform_bus_type, NULL, NULL, + exynos_pd_init_platform_dev); } arch_initcall(exynos4_pm_init_power_domain); From fa2c5f2f4fc5c5530b902e772f88af099da2acb0 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 23 Jan 2015 16:51:13 +0100 Subject: [PATCH 654/788] ARM: dts: exynos4: add sysmmu nodes This patch adds System MMU nodes that are specific to Exynos4210/4x12 series. Signed-off-by: Marek Szyprowski --- arch/arm/boot/dts/exynos4.dtsi | 118 ++++++++++++++++++++++++++++++ arch/arm/boot/dts/exynos4210.dtsi | 23 ++++++ arch/arm/boot/dts/exynos4x12.dtsi | 82 +++++++++++++++++++++ 3 files changed, 223 insertions(+) diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi index e4a3e8b8c43915..526fb2fb2298e5 100644 --- a/arch/arm/boot/dts/exynos4.dtsi +++ b/arch/arm/boot/dts/exynos4.dtsi @@ -186,6 +186,7 @@ clock-names = "fimc", "sclk_fimc"; power-domains = <&pd_cam>; samsung,sysreg = <&sys_reg>; + iommus = <&sysmmu_fimc0>; status = "disabled"; }; @@ -197,6 +198,7 @@ clock-names = "fimc", "sclk_fimc"; power-domains = <&pd_cam>; samsung,sysreg = <&sys_reg>; + iommus = <&sysmmu_fimc1>; status = "disabled"; }; @@ -208,6 +210,7 @@ clock-names = "fimc", "sclk_fimc"; power-domains = <&pd_cam>; samsung,sysreg = <&sys_reg>; + iommus = <&sysmmu_fimc2>; status = "disabled"; }; @@ -219,6 +222,7 @@ clock-names = "fimc", "sclk_fimc"; power-domains = <&pd_cam>; samsung,sysreg = <&sys_reg>; + iommus = <&sysmmu_fimc3>; status = "disabled"; }; @@ -413,6 +417,8 @@ clocks = <&clock CLK_MFC>, <&clock CLK_SCLK_MFC>; clock-names = "mfc", "sclk_mfc"; status = "disabled"; + iommus = <&sysmmu_mfc_l>, <&sysmmu_mfc_r>; + iommu-names = "left", "right"; }; serial_0: serial@13800000 { @@ -678,6 +684,7 @@ power-domains = <&pd_lcd0>; samsung,sysreg = <&sys_reg>; status = "disabled"; + iommus = <&sysmmu_fimd0>; }; sss@10830000 { @@ -708,9 +715,120 @@ interrupts = <0 91 0>; reg = <0x12C10000 0x2100>, <0x12c00000 0x300>; power-domains = <&pd_tv>; + iommus = <&sysmmu_tv>; status = "disabled"; }; + sysmmu_mfc_l: sysmmu@13620000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x13620000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <5 5>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_MFCL>, <&clock CLK_MFC>; + power-domains = <&pd_mfc>; + #iommu-cells = <0>; + }; + + sysmmu_mfc_r: sysmmu@13630000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x13630000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <5 6>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_MFCR>, <&clock CLK_MFC>; + power-domains = <&pd_mfc>; + #iommu-cells = <0>; + }; + + sysmmu_tv: sysmmu@12E20000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x12E20000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <5 4>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_TV>, <&clock CLK_MIXER>; + power-domains = <&pd_tv>; + #iommu-cells = <0>; + }; + + sysmmu_fimc0: sysmmu@11A20000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x11A20000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <4 2>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_FIMC0>, <&clock CLK_FIMC0>; + power-domains = <&pd_cam>; + #iommu-cells = <0>; + }; + + sysmmu_fimc1: sysmmu@11A30000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x11A30000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <4 3>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_FIMC1>, <&clock CLK_FIMC1>; + power-domains = <&pd_cam>; + #iommu-cells = <0>; + }; + + sysmmu_fimc2: sysmmu@11A40000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x11A40000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <4 4>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_FIMC2>, <&clock CLK_FIMC2>; + power-domains = <&pd_cam>; + #iommu-cells = <0>; + }; + + sysmmu_fimc3: sysmmu@11A50000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x11A50000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <4 5>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_FIMC3>, <&clock CLK_FIMC3>; + power-domains = <&pd_cam>; + #iommu-cells = <0>; + }; + + sysmmu_jpeg: sysmmu@11A60000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x11A60000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <4 6>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_JPEG>, <&clock CLK_JPEG>; + power-domains = <&pd_cam>; + #iommu-cells = <0>; + }; + + sysmmu_rotator: sysmmu@12A30000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x12A30000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <5 0>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_ROTATOR>, <&clock CLK_ROTATOR>; + power-domains = <&pd_lcd0>; + #iommu-cells = <0>; + }; + + sysmmu_fimd0: sysmmu@11E20000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x11E20000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <5 2>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_FIMD0>, <&clock CLK_FIMD0>; + power-domains = <&pd_lcd0>; + #iommu-cells = <0>; + }; + ppmu_dmc0: ppmu_dmc0@106a0000 { compatible = "samsung,exynos-ppmu"; reg = <0x106a0000 0x2000>; diff --git a/arch/arm/boot/dts/exynos4210.dtsi b/arch/arm/boot/dts/exynos4210.dtsi index be89f83f70e775..19a8e711e61627 100644 --- a/arch/arm/boot/dts/exynos4210.dtsi +++ b/arch/arm/boot/dts/exynos4210.dtsi @@ -195,6 +195,7 @@ interrupts = <0 89 0>; clocks = <&clock CLK_SCLK_FIMG2D>, <&clock CLK_G2D>; clock-names = "sclk_fimg2d", "fimg2d"; + iommus = <&sysmmu_g2d>; status = "disabled"; }; @@ -244,4 +245,26 @@ clock-names = "ppmu"; status = "disabled"; }; + + sysmmu_g2d: sysmmu@12A20000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x12A20000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <4 7>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_G2D>, <&clock CLK_G2D>; + power-domains = <&pd_lcd0>; + #iommu-cells = <0>; + }; + + sysmmu_fimd1: sysmmu@12220000 { + compatible = "samsung,exynos-sysmmu"; + interrupt-parent = <&combiner>; + reg = <0x12220000 0x1000>; + interrupts = <5 3>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_FIMD1>, <&clock CLK_FIMD1>; + power-domains = <&pd_lcd1>; + #iommu-cells = <0>; + }; }; diff --git a/arch/arm/boot/dts/exynos4x12.dtsi b/arch/arm/boot/dts/exynos4x12.dtsi index 6a6abe14fd9b59..6c664bae0d9fcb 100644 --- a/arch/arm/boot/dts/exynos4x12.dtsi +++ b/arch/arm/boot/dts/exynos4x12.dtsi @@ -165,6 +165,7 @@ interrupts = <0 89 0>; clocks = <&clock CLK_SCLK_FIMG2D>, <&clock CLK_G2D>; clock-names = "sclk_fimg2d", "fimg2d"; + iommus = <&sysmmu_g2d>; status = "disabled"; }; @@ -214,6 +215,7 @@ power-domains = <&pd_isp>; clocks = <&clock CLK_FIMC_LITE0>; clock-names = "flite"; + iommus = <&sysmmu_fimc_lite0>; status = "disabled"; }; @@ -224,6 +226,7 @@ power-domains = <&pd_isp>; clocks = <&clock CLK_FIMC_LITE1>; clock-names = "flite"; + iommus = <&sysmmu_fimc_lite1>; status = "disabled"; }; @@ -252,6 +255,9 @@ "mcuispdiv1", "uart", "aclk200", "div_aclk200", "aclk400mcuisp", "div_aclk400mcuisp"; + iommus = <&sysmmu_fimc_isp>, <&sysmmu_fimc_drc>, + <&sysmmu_fimc_fd>, <&sysmmu_fimc_mcuctl>; + iommu-names = "isp", "drc", "fd", "mcuctl"; #address-cells = <1>; #size-cells = <1>; ranges; @@ -309,4 +315,80 @@ clocks = <&clock CLK_MIXER>, <&clock CLK_HDMI>, <&clock CLK_SCLK_HDMI>, <&clock CLK_VP>; }; + + sysmmu_g2d: sysmmu@10A40000{ + compatible = "samsung,exynos-sysmmu"; + reg = <0x10A40000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <4 7>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_G2D>, <&clock CLK_G2D>; + #iommu-cells = <0>; + }; + + sysmmu_fimc_isp: sysmmu@12260000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x12260000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <16 2>; + power-domains = <&pd_isp>; + clock-names = "sysmmu"; + clocks = <&clock CLK_SMMU_ISP>; + #iommu-cells = <0>; + }; + + sysmmu_fimc_drc: sysmmu@12270000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x12270000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <16 3>; + power-domains = <&pd_isp>; + clock-names = "sysmmu"; + clocks = <&clock CLK_SMMU_DRC>; + #iommu-cells = <0>; + }; + + sysmmu_fimc_fd: sysmmu@122A0000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x122A0000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <16 4>; + power-domains = <&pd_isp>; + clock-names = "sysmmu"; + clocks = <&clock CLK_SMMU_FD>; + #iommu-cells = <0>; + }; + + sysmmu_fimc_mcuctl: sysmmu@122B0000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x122B0000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <16 5>; + power-domains = <&pd_isp>; + clock-names = "sysmmu"; + clocks = <&clock CLK_SMMU_ISPCX>; + #iommu-cells = <0>; + }; + + sysmmu_fimc_lite0: sysmmu@123B0000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x123B0000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <16 0>; + power-domains = <&pd_isp>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_LITE0>, <&clock CLK_FIMC_LITE0>; + #iommu-cells = <0>; + }; + + sysmmu_fimc_lite1: sysmmu@123C0000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x123C0000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <16 1>; + power-domains = <&pd_isp>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_LITE1>, <&clock CLK_FIMC_LITE1>; + #iommu-cells = <0>; + }; }; From 2e27fbc0ee0a0e23d06bc9203c7581e6c10afc33 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 23 Jan 2015 16:51:14 +0100 Subject: [PATCH 655/788] ARM: dts: exynos5250: add sysmmu nodes Signed-off-by: Marek Szyprowski --- arch/arm/boot/dts/exynos5250.dtsi | 250 ++++++++++++++++++++++++++++++ 1 file changed, 250 insertions(+) diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index adbde1adad95dd..689aa2ce375930 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -227,6 +227,7 @@ interrupts = <0 91 0>; clocks = <&clock CLK_G2D>; clock-names = "fimg2d"; + iommus = <&sysmmu_g2d>; }; mfc: codec@11000000 { @@ -236,6 +237,8 @@ power-domains = <&pd_mfc>; clocks = <&clock CLK_MFC>; clock-names = "mfc"; + iommus = <&sysmmu_mfc_l>, <&sysmmu_mfc_r>; + iommu-names = "left", "right"; }; rtc: rtc@101E0000 { @@ -716,6 +719,7 @@ power-domains = <&pd_gsc>; clocks = <&clock CLK_GSCL0>; clock-names = "gscl"; + iommu = <&sysmmu_gsc1>; }; gsc_1: gsc@13e10000 { @@ -725,6 +729,7 @@ power-domains = <&pd_gsc>; clocks = <&clock CLK_GSCL1>; clock-names = "gscl"; + iommu = <&sysmmu_gsc1>; }; gsc_2: gsc@13e20000 { @@ -734,6 +739,7 @@ power-domains = <&pd_gsc>; clocks = <&clock CLK_GSCL2>; clock-names = "gscl"; + iommu = <&sysmmu_gsc2>; }; gsc_3: gsc@13e30000 { @@ -743,6 +749,7 @@ power-domains = <&pd_gsc>; clocks = <&clock CLK_GSCL3>; clock-names = "gscl"; + iommu = <&sysmmu_gsc3>; }; hdmi: hdmi { @@ -766,6 +773,7 @@ clocks = <&clock CLK_MIXER>, <&clock CLK_HDMI>, <&clock CLK_SCLK_HDMI>; clock-names = "mixer", "hdmi", "sclk_hdmi"; + iommus = <&sysmmu_tv>; }; dp_phy: video-phy@10040720 { @@ -786,6 +794,7 @@ power-domains = <&pd_disp1>; clocks = <&clock CLK_SCLK_FIMD1>, <&clock CLK_FIMD1>; clock-names = "sclk_fimd", "fimd"; + iommus = <&sysmmu_fimd1>; }; adc: adc@12D10000 { @@ -807,4 +816,245 @@ clocks = <&clock CLK_SSS>; clock-names = "secss"; }; + + sysmmu_gsc0: sysmmu@13E80000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x13E80000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <2 0>; + power-domains = <&pd_gsc>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_GSCL0>, <&clock CLK_GSCL0>; + #iommu-cells = <0>; + }; + + sysmmu_gsc1: sysmmu@13E90000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x13E90000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <2 2>; + power-domains = <&pd_gsc>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_GSCL1>, <&clock CLK_GSCL1>; + #iommu-cells = <0>; + }; + + sysmmu_gsc2: sysmmu@13EA0000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x13EA0000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <2 4>; + power-domains = <&pd_gsc>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_GSCL2>, <&clock CLK_GSCL2>; + #iommu-cells = <0>; + }; + + sysmmu_gsc3: sysmmu@13EB0000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x13EB0000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <2 6>; + power-domains = <&pd_gsc>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_GSCL3>, <&clock CLK_GSCL3>; + #iommu-cells = <0>; + }; + + sysmmu_mfc_r: sysmmu@11200000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x11200000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <6 2>; + power-domains = <&pd_mfc>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_MFCR>, <&clock CLK_MFC>; + #iommu-cells = <0>; + }; + + sysmmu_mfc_l: sysmmu@11210000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x11210000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <8 5>; + power-domains = <&pd_mfc>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_MFCL>, <&clock CLK_MFC>; + #iommu-cells = <0>; + }; + + sysmmu_tv: sysmmu@14650000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x14650000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <7 4>; + power-domains = <&pd_disp1>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_TV>, <&clock CLK_MIXER>; + #iommu-cells = <0>; + }; + + sysmmu_fimd1: sysmmu@14640000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x14640000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <3 2>; + power-domains = <&pd_disp1>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_FIMD1>, <&clock CLK_FIMD1>; + #iommu-cells = <0>; + }; + + sysmmu_g2d: sysmmu@10A60000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x10A60000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <24 5>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_2D>, <&clock CLK_G2D>; + #iommu-cells = <0>; + }; + + sysmmu_rotator: sysmmu@11D40000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x11D40000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <4 0>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_ROTATOR>, <&clock CLK_ROTATOR>; + #iommu-cells = <0>; + }; + + sysmmu_jpeg: sysmmu@11F20000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x11F20000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <4 2>; + power-domains = <&pd_gsc>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_JPEG>, <&clock CLK_JPEG>; + #iommu-cells = <0>; + }; + + sysmmu_fimc_isp: sysmmu@13260000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x13260000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <10 6>; + clock-names = "sysmmu"; + clocks = <&clock CLK_SMMU_FIMC_ISP>; + #iommu-cells = <0>; + }; + + sysmmu_fimc_drc: sysmmu@13270000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x13270000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <11 6>; + clock-names = "sysmmu"; + clocks = <&clock CLK_SMMU_FIMC_DRC>; + #iommu-cells = <0>; + }; + + sysmmu_fimc_fd: sysmmu@132A0000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x132A0000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <5 0>; + clock-names = "sysmmu"; + clocks = <&clock CLK_SMMU_FIMC_FD>; + #iommu-cells = <0>; + }; + + sysmmu_fimc_scc: sysmmu@13280000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x13280000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <5 2>; + clock-names = "sysmmu"; + clocks = <&clock CLK_SMMU_FIMC_SCC>; + #iommu-cells = <0>; + }; + + sysmmu_fimc_scp: sysmmu@13290000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x13290000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <3 6>; + clock-names = "sysmmu"; + clocks = <&clock CLK_SMMU_FIMC_SCP>; + #iommu-cells = <0>; + }; + + sysmmu_fimc_mcuctl: sysmmu@132B0000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x132B0000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <5 4>; + clock-names = "sysmmu"; + clocks = <&clock CLK_SMMU_FIMC_MCU>; + #iommu-cells = <0>; + }; + + sysmmu_fimc_odc: sysmmu@132C0000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x132C0000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <11 0>; + clock-names = "sysmmu"; + clocks = <&clock CLK_SMMU_FIMC_ODC>; + #iommu-cells = <0>; + }; + + sysmmu_fimc_dis0: sysmmu@132D0000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x132D0000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <10 4>; + clock-names = "sysmmu"; + clocks = <&clock CLK_SMMU_FIMC_DIS0>; + #iommu-cells = <0>; + }; + + sysmmu_fimc_dis1: sysmmu@132E0000{ + compatible = "samsung,exynos-sysmmu"; + reg = <0x132E0000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <9 4>; + clock-names = "sysmmu"; + clocks = <&clock CLK_SMMU_FIMC_DIS1>; + #iommu-cells = <0>; + }; + + sysmmu_fimc_3dnr: sysmmu@132F0000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x132F0000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <5 6>; + clock-names = "sysmmu"; + clocks = <&clock CLK_SMMU_FIMC_3DNR>; + #iommu-cells = <0>; + }; + + sysmmu_fimc_lite0: sysmmu@13C40000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x13C40000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <3 4>; + power-domains = <&pd_gsc>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_FIMC_LITE0>, <&clock CLK_CAMIF_TOP>; + #iommu-cells = <0>; + }; + + sysmmu_fimc_lite1: sysmmu@13C50000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x13C50000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <24 1>; + power-domains = <&pd_gsc>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_FIMC_LITE1>, <&clock CLK_CAMIF_TOP>; + #iommu-cells = <0>; + }; }; From 7f3787432f558c63ae2c0c2a6dcc9c959bca8818 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 23 Jan 2015 16:51:15 +0100 Subject: [PATCH 656/788] ARM: dts: exynos5420: add sysmmu nodes Signed-off-by: Marek Szyprowski --- arch/arm/boot/dts/exynos5420.dtsi | 181 ++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) diff --git a/arch/arm/boot/dts/exynos5420.dtsi b/arch/arm/boot/dts/exynos5420.dtsi index c41a398afa2914..b42f4bb7c314dc 100644 --- a/arch/arm/boot/dts/exynos5420.dtsi +++ b/arch/arm/boot/dts/exynos5420.dtsi @@ -179,6 +179,8 @@ clocks = <&clock CLK_MFC>; clock-names = "mfc"; power-domains = <&mfc_pd>; + iommus = <&sysmmu_mfc_l>, <&sysmmu_mfc_r>; + iommu-names = "left", "right"; }; mmc_0: mmc@12200000 { @@ -541,6 +543,8 @@ fimd: fimd@14400000 { clocks = <&clock CLK_SCLK_FIMD1>, <&clock CLK_FIMD1>; clock-names = "sclk_fimd", "fimd"; + iommus = <&sysmmu_fimd1_0>, <&sysmmu_fimd1_1>; + iommu-names = "m0", "m1"; }; adc: adc@12D10000 { @@ -727,6 +731,7 @@ clocks = <&clock CLK_MIXER>, <&clock CLK_HDMI>, <&clock CLK_SCLK_HDMI>; clock-names = "mixer", "hdmi", "sclk_hdmi"; + iommus = <&sysmmu_tv>; }; gsc_0: video-scaler@13e00000 { @@ -736,6 +741,7 @@ clocks = <&clock CLK_GSCL0>; clock-names = "gscl"; power-domains = <&gsc_pd>; + iommus = <&sysmmu_gscl0>; }; gsc_1: video-scaler@13e10000 { @@ -745,6 +751,7 @@ clocks = <&clock CLK_GSCL1>; clock-names = "gscl"; power-domains = <&gsc_pd>; + iommus = <&sysmmu_gscl1>; }; pmu_system_controller: system-controller@10040000 { @@ -936,4 +943,178 @@ samsung,sysreg-phandle = <&sysreg_system_controller>; samsung,pmureg-phandle = <&pmu_system_controller>; }; + + sysmmu_g2dr: sysmmu@0x10A60000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x10A60000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <24 5>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_G2D>, <&clock CLK_G2D>; + #iommu-cells = <0>; + }; + + sysmmu_g2dw: sysmmu@0x10A70000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x10A70000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <22 2>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_G2D>, <&clock CLK_G2D>; + #iommu-cells = <0>; + }; + + sysmmu_tv: sysmmu@0x14650000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x14650000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <7 4>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_MIXER>, <&clock CLK_MIXER>; + samsung,power-domain = <&disp_pd>; + #iommu-cells = <0>; + }; + + sysmmu_gscl0: sysmmu@0x13E80000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x13E80000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <2 0>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_GSCL0>, <&clock CLK_GSCL0>; + power-domains = <&gsc_pd>; + #iommu-cells = <0>; + }; + + sysmmu_gscl1: sysmmu@0x13E90000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x13E90000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <2 2>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_GSCL1>, <&clock CLK_GSCL1>; + power-domains = <&gsc_pd>; + #iommu-cells = <0>; + }; + + sysmmu_scaler0r: sysmmu@0x12880000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x12880000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <22 4>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_MSCL0>, <&clock CLK_MSCL0>; + #iommu-cells = <0>; + }; + + sysmmu_scaler1r: sysmmu@0x12890000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x12890000 0x1000>; + interrupts = <0 186 0>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_MSCL1>, <&clock CLK_MSCL1>; + #iommu-cells = <0>; + }; + + sysmmu_scaler2r: sysmmu@0x128A0000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x128A0000 0x1000>; + interrupts = <0 188 0>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_MSCL2>, <&clock CLK_MSCL2>; + #iommu-cells = <0>; + }; + + sysmmu_scaler0w: sysmmu@0x128C0000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x128C0000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <27 2>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_MSCL0>, <&clock CLK_MSCL0>; + #iommu-cells = <0>; + }; + + sysmmu_scaler1w: sysmmu@0x128D0000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x128D0000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <22 6>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_MSCL1>, <&clock CLK_MSCL1>; + #iommu-cells = <0>; + }; + + sysmmu_scaler2w: sysmmu@0x128E0000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x128E0000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <19 6>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_MSCL2>, <&clock CLK_MSCL2>; + #iommu-cells = <0>; + }; + + sysmmu_jpeg: sysmmu@0x11F10000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x11F10000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <4 2>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_JPEG>, <&clock CLK_JPEG>; + #iommu-cells = <0>; + }; + + sysmmu_jpeg2: sysmmu@0x11F20000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x11F20000 0x1000>; + interrupts = <0 169 0>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_JPEG2>, <&clock CLK_JPEG2>; + #iommu-cells = <0>; + }; + + sysmmu_mfc_l: sysmmu@0x11200000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x11200000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <6 2>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_MFCL>, <&clock CLK_MFC>; + power-domains = <&mfc_pd>; + #iommu-cells = <0>; + }; + + sysmmu_mfc_r: sysmmu@0x11210000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x11210000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <8 5>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_MFCR>, <&clock CLK_MFC>; + power-domains = <&mfc_pd>; + #iommu-cells = <0>; + }; + + sysmmu_fimd1_0: sysmmu@0x14640000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x14640000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <3 2>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_FIMD1M0>, <&clock CLK_FIMD1>; + samsung,power-domain = <&disp_pd>; + #iommu-cells = <0>; + }; + + sysmmu_fimd1_1: sysmmu@0x14680000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x14680000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <3 0>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_FIMD1M0>, <&clock CLK_FIMD1>; + samsung,power-domain = <&disp_pd>; + #iommu-cells = <0>; + }; }; From 09416031d043af15cec801c8e3ae92112bdfeb79 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 23 Jan 2015 16:51:16 +0100 Subject: [PATCH 657/788] iommu: exynos: don't read version register on every tlb operation This patch removes reading of REG_MMU_VERSION register on every tlb operation and caches SYSMMU version in driver's internal data. Signed-off-by: Marek Szyprowski --- drivers/iommu/exynos-iommu.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 7ce52737c7a129..b6c86121cdccde 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -212,6 +212,7 @@ struct sysmmu_drvdata { spinlock_t lock; struct iommu_domain *domain; phys_addr_t pgtable; + int version; }; static bool set_sysmmu_active(struct sysmmu_drvdata *data) @@ -238,11 +239,6 @@ static void sysmmu_unblock(void __iomem *sfrbase) __raw_writel(CTRL_ENABLE, sfrbase + REG_MMU_CTRL); } -static unsigned int __raw_sysmmu_version(struct sysmmu_drvdata *data) -{ - return MMU_RAW_VER(__raw_readl(data->sfrbase + REG_MMU_VERSION)); -} - static bool sysmmu_block(void __iomem *sfrbase) { int i = 120; @@ -402,7 +398,7 @@ static void __sysmmu_init_config(struct sysmmu_drvdata *data) unsigned int cfg = CFG_LRU | CFG_QOS(15); unsigned int ver; - ver = __raw_sysmmu_version(data); + ver = MMU_RAW_VER(__raw_readl(data->sfrbase + REG_MMU_VERSION)); if (MMU_MAJ_VER(ver) == 3) { if (MMU_MIN_VER(ver) >= 2) { cfg |= CFG_FLPDCACHE; @@ -416,6 +412,7 @@ static void __sysmmu_init_config(struct sysmmu_drvdata *data) } __raw_writel(cfg, data->sfrbase + REG_MMU_CFG); + data->version = ver; } static void __sysmmu_enable_nocount(struct sysmmu_drvdata *data) @@ -525,7 +522,7 @@ static bool exynos_sysmmu_disable(struct device *dev) static void __sysmmu_tlb_invalidate_flpdcache(struct sysmmu_drvdata *data, sysmmu_iova_t iova) { - if (__raw_sysmmu_version(data) == MAKE_MMU_VER(3, 3)) + if (data->version == MAKE_MMU_VER(3, 3)) __raw_writel(iova | 0x1, data->sfrbase + REG_MMU_FLUSH_ENTRY); } @@ -574,7 +571,7 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, sysmmu_iova_t iova, * 1MB page can be cached in one of all sets. * 64KB page can be one of 16 consecutive sets. */ - if (MMU_MAJ_VER(__raw_sysmmu_version(data)) == 2) + if (MMU_MAJ_VER(data->version) == 2) num_inv = min_t(unsigned int, size / PAGE_SIZE, 64); if (sysmmu_block(data->sfrbase)) { From b4c5a86281c9f5c59e87b5f266731c9bbbf91f1a Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 23 Jan 2015 16:51:17 +0100 Subject: [PATCH 658/788] iommu: exynos: remove unused functions This patch removes two unneeded functions, which are not a part of generic IOMMU API and were never used by any other driver. Signed-off-by: Marek Szyprowski --- drivers/iommu/exynos-iommu.c | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index b6c86121cdccde..3c824114771ad2 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -490,13 +490,6 @@ static int __exynos_sysmmu_enable(struct device *dev, phys_addr_t pgtable, return ret; } -int exynos_sysmmu_enable(struct device *dev, phys_addr_t pgtable) -{ - BUG_ON(!memblock_is_memory(pgtable)); - - return __exynos_sysmmu_enable(dev, pgtable, NULL); -} - static bool exynos_sysmmu_disable(struct device *dev) { unsigned long flags; @@ -588,30 +581,6 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, sysmmu_iova_t iova, spin_unlock_irqrestore(&data->lock, flags); } -void exynos_sysmmu_tlb_invalidate(struct device *dev) -{ - struct exynos_iommu_owner *owner = dev->archdata.iommu; - unsigned long flags; - struct sysmmu_drvdata *data; - - data = dev_get_drvdata(owner->sysmmu); - - spin_lock_irqsave(&data->lock, flags); - if (is_sysmmu_active(data)) { - if (!IS_ERR(data->clk_master)) - clk_enable(data->clk_master); - if (sysmmu_block(data->sfrbase)) { - __sysmmu_tlb_invalidate(data->sfrbase); - sysmmu_unblock(data->sfrbase); - } - if (!IS_ERR(data->clk_master)) - clk_disable(data->clk_master); - } else { - dev_dbg(dev, "disabled. Skipping TLB invalidation\n"); - } - spin_unlock_irqrestore(&data->lock, flags); -} - static int __init exynos_sysmmu_probe(struct platform_device *pdev) { int irq, ret; From 17d8e01a00cd0513a888fb1cdbaeff3570e88a2c Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 23 Jan 2015 16:51:18 +0100 Subject: [PATCH 659/788] iommu: exynos: remove useless spinlock This patch removes useless spinlocks and other unused members from struct exynos_iommu_owner. There is no point is protecting this structure by spinlock because content of this structure doesn't change and other structures have their own spinlocks. Signed-off-by: Marek Szyprowski --- drivers/iommu/exynos-iommu.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 3c824114771ad2..aa8c4b0ae2a14e 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -189,9 +189,6 @@ struct exynos_iommu_owner { struct list_head client; /* entry of exynos_iommu_domain.clients */ struct device *dev; struct device *sysmmu; - struct iommu_domain *domain; - void *vmm_data; /* IO virtual memory manager's data */ - spinlock_t lock; /* Lock to preserve consistency of System MMU */ }; struct exynos_iommu_domain { @@ -477,16 +474,12 @@ static int __exynos_sysmmu_enable(struct device *dev, phys_addr_t pgtable, BUG_ON(!has_sysmmu(dev)); - spin_lock_irqsave(&owner->lock, flags); - data = dev_get_drvdata(owner->sysmmu); ret = __sysmmu_enable(data, pgtable, domain); if (ret >= 0) data->master = dev; - spin_unlock_irqrestore(&owner->lock, flags); - return ret; } @@ -499,16 +492,12 @@ static bool exynos_sysmmu_disable(struct device *dev) BUG_ON(!has_sysmmu(dev)); - spin_lock_irqsave(&owner->lock, flags); - data = dev_get_drvdata(owner->sysmmu); disabled = __sysmmu_disable(data); if (disabled) data->master = NULL; - spin_unlock_irqrestore(&owner->lock, flags); - return disabled; } From c92ea7f8d205c3b8e1e98f3aab77886c20e37546 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 23 Jan 2015 16:51:19 +0100 Subject: [PATCH 660/788] iommu: exynos: refactor function parameters to simplify code This patch simplifies the code by: - refactoring function parameters from struct device pointer to direct pointer to struct sysmmu drvdata - moving list_head enteries from struct exynos_iommu_owner directly to struct sysmmu_drvdata Signed-off-by: Marek Szyprowski --- drivers/iommu/exynos-iommu.c | 93 ++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 47 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index aa8c4b0ae2a14e..862261fe097bec 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -186,8 +186,6 @@ static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = { /* attached to dev.archdata.iommu of the master device */ struct exynos_iommu_owner { - struct list_head client; /* entry of exynos_iommu_domain.clients */ - struct device *dev; struct device *sysmmu; }; @@ -208,6 +206,7 @@ struct sysmmu_drvdata { int activations; spinlock_t lock; struct iommu_domain *domain; + struct list_head domain_node; phys_addr_t pgtable; int version; }; @@ -508,12 +507,10 @@ static void __sysmmu_tlb_invalidate_flpdcache(struct sysmmu_drvdata *data, __raw_writel(iova | 0x1, data->sfrbase + REG_MMU_FLUSH_ENTRY); } -static void sysmmu_tlb_invalidate_flpdcache(struct device *dev, +static void sysmmu_tlb_invalidate_flpdcache(struct sysmmu_drvdata *data, sysmmu_iova_t iova) { unsigned long flags; - struct exynos_iommu_owner *owner = dev->archdata.iommu; - struct sysmmu_drvdata *data = dev_get_drvdata(owner->sysmmu); if (!IS_ERR(data->clk_master)) clk_enable(data->clk_master); @@ -527,14 +524,10 @@ static void sysmmu_tlb_invalidate_flpdcache(struct device *dev, clk_disable(data->clk_master); } -static void sysmmu_tlb_invalidate_entry(struct device *dev, sysmmu_iova_t iova, - size_t size) +static void sysmmu_tlb_invalidate_entry(struct sysmmu_drvdata *data, + sysmmu_iova_t iova, size_t size) { - struct exynos_iommu_owner *owner = dev->archdata.iommu; unsigned long flags; - struct sysmmu_drvdata *data; - - data = dev_get_drvdata(owner->sysmmu); spin_lock_irqsave(&data->lock, flags); if (is_sysmmu_active(data)) { @@ -564,8 +557,8 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, sysmmu_iova_t iova, if (!IS_ERR(data->clk_master)) clk_disable(data->clk_master); } else { - dev_dbg(dev, "disabled. Skipping TLB invalidation @ %#x\n", - iova); + dev_dbg(data->master, + "disabled. Skipping TLB invalidation @ %#x\n", iova); } spin_unlock_irqrestore(&data->lock, flags); } @@ -703,7 +696,7 @@ static int exynos_iommu_domain_init(struct iommu_domain *domain) static void exynos_iommu_domain_destroy(struct iommu_domain *domain) { struct exynos_iommu_domain *priv = domain->priv; - struct exynos_iommu_owner *owner; + struct sysmmu_drvdata *data; unsigned long flags; int i; @@ -711,14 +704,12 @@ static void exynos_iommu_domain_destroy(struct iommu_domain *domain) spin_lock_irqsave(&priv->lock, flags); - list_for_each_entry(owner, &priv->clients, client) { - while (!exynos_sysmmu_disable(owner->dev)) - ; /* until System MMU is actually disabled */ + list_for_each_entry(data, &priv->clients, domain_node) { + if (__sysmmu_disable(data)) + data->master = NULL; + list_del_init(&data->domain_node); } - while (!list_empty(&priv->clients)) - list_del_init(priv->clients.next); - spin_unlock_irqrestore(&priv->lock, flags); for (i = 0; i < NUM_LV1ENTRIES; i++) @@ -737,20 +728,26 @@ static int exynos_iommu_attach_device(struct iommu_domain *domain, { struct exynos_iommu_owner *owner = dev->archdata.iommu; struct exynos_iommu_domain *priv = domain->priv; + struct sysmmu_drvdata *data; phys_addr_t pagetable = virt_to_phys(priv->pgtable); unsigned long flags; - int ret; + int ret = -ENODEV; - spin_lock_irqsave(&priv->lock, flags); + if (!has_sysmmu(dev)) + return -ENODEV; - ret = __exynos_sysmmu_enable(dev, pagetable, domain); - if (ret == 0) { - list_add_tail(&owner->client, &priv->clients); - owner->domain = domain; + data = dev_get_drvdata(owner->sysmmu); + if (data) { + ret = __sysmmu_enable(data, pagetable, domain); + if (ret >= 0) { + data->master = dev; + + spin_lock_irqsave(&priv->lock, flags); + list_add_tail(&data->domain_node, &priv->clients); + spin_unlock_irqrestore(&priv->lock, flags); + } } - spin_unlock_irqrestore(&priv->lock, flags); - if (ret < 0) { dev_err(dev, "%s: Failed to attach IOMMU with pgtable %pa\n", __func__, &pagetable); @@ -766,26 +763,29 @@ static int exynos_iommu_attach_device(struct iommu_domain *domain, static void exynos_iommu_detach_device(struct iommu_domain *domain, struct device *dev) { - struct exynos_iommu_owner *owner; struct exynos_iommu_domain *priv = domain->priv; phys_addr_t pagetable = virt_to_phys(priv->pgtable); + struct sysmmu_drvdata *data; unsigned long flags; + int found = 0; - spin_lock_irqsave(&priv->lock, flags); + if (!has_sysmmu(dev)) + return; - list_for_each_entry(owner, &priv->clients, client) { - if (owner == dev->archdata.iommu) { - if (exynos_sysmmu_disable(dev)) { - list_del_init(&owner->client); - owner->domain = NULL; + spin_lock_irqsave(&priv->lock, flags); + list_for_each_entry(data, &priv->clients, domain_node) { + if (data->master == dev) { + if (__sysmmu_disable(data)) { + data->master = NULL; + list_del_init(&data->domain_node); } + found = true; break; } } - spin_unlock_irqrestore(&priv->lock, flags); - if (owner == dev->archdata.iommu) + if (found) dev_dbg(dev, "%s: Detached IOMMU with pgtable %pa\n", __func__, &pagetable); else @@ -832,12 +832,11 @@ static sysmmu_pte_t *alloc_lv2entry(struct exynos_iommu_domain *priv, * not currently mapped. */ if (need_flush_flpd_cache) { - struct exynos_iommu_owner *owner; + struct sysmmu_drvdata *data; spin_lock(&priv->lock); - list_for_each_entry(owner, &priv->clients, client) - sysmmu_tlb_invalidate_flpdcache( - owner->dev, iova); + list_for_each_entry(data, &priv->clients, domain_node) + sysmmu_tlb_invalidate_flpdcache(data, iova); spin_unlock(&priv->lock); } } @@ -872,13 +871,13 @@ static int lv1set_section(struct exynos_iommu_domain *priv, spin_lock(&priv->lock); if (lv1ent_page_zero(sent)) { - struct exynos_iommu_owner *owner; + struct sysmmu_drvdata *data; /* * Flushing FLPD cache in System MMU v3.3 that may cache a FLPD * entry by speculative prefetch of SLPD which has no mapping. */ - list_for_each_entry(owner, &priv->clients, client) - sysmmu_tlb_invalidate_flpdcache(owner->dev, iova); + list_for_each_entry(data, &priv->clients, domain_node) + sysmmu_tlb_invalidate_flpdcache(data, iova); } spin_unlock(&priv->lock); @@ -983,13 +982,13 @@ static int exynos_iommu_map(struct iommu_domain *domain, unsigned long l_iova, static void exynos_iommu_tlb_invalidate_entry(struct exynos_iommu_domain *priv, sysmmu_iova_t iova, size_t size) { - struct exynos_iommu_owner *owner; + struct sysmmu_drvdata *data; unsigned long flags; spin_lock_irqsave(&priv->lock, flags); - list_for_each_entry(owner, &priv->clients, client) - sysmmu_tlb_invalidate_entry(owner->dev, iova, size); + list_for_each_entry(data, &priv->clients, domain_node) + sysmmu_tlb_invalidate_entry(data, iova, size); spin_unlock_irqrestore(&priv->lock, flags); } From 8f44dc98bee070d7efd62b06a99ee3d97416ea62 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 23 Jan 2015 16:51:20 +0100 Subject: [PATCH 661/788] iommu: exynos: remove unused functions, part 2 After refactoring functions to use pointer to struct sysmmu_drvdata directly, some functions became useless and thus never used, so remove them completely. Signed-off-by: Marek Szyprowski --- drivers/iommu/exynos-iommu.c | 43 ------------------------------------ 1 file changed, 43 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 862261fe097bec..e62cb96a742c3a 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -457,49 +457,6 @@ static int __sysmmu_enable(struct sysmmu_drvdata *data, return ret; } -/* __exynos_sysmmu_enable: Enables System MMU - * - * returns -error if an error occurred and System MMU is not enabled, - * 0 if the System MMU has been just enabled and 1 if System MMU was already - * enabled before. - */ -static int __exynos_sysmmu_enable(struct device *dev, phys_addr_t pgtable, - struct iommu_domain *domain) -{ - int ret = 0; - unsigned long flags; - struct exynos_iommu_owner *owner = dev->archdata.iommu; - struct sysmmu_drvdata *data; - - BUG_ON(!has_sysmmu(dev)); - - data = dev_get_drvdata(owner->sysmmu); - - ret = __sysmmu_enable(data, pgtable, domain); - if (ret >= 0) - data->master = dev; - - return ret; -} - -static bool exynos_sysmmu_disable(struct device *dev) -{ - unsigned long flags; - bool disabled = true; - struct exynos_iommu_owner *owner = dev->archdata.iommu; - struct sysmmu_drvdata *data; - - BUG_ON(!has_sysmmu(dev)); - - data = dev_get_drvdata(owner->sysmmu); - - disabled = __sysmmu_disable(data); - if (disabled) - data->master = NULL; - - return disabled; -} - static void __sysmmu_tlb_invalidate_flpdcache(struct sysmmu_drvdata *data, sysmmu_iova_t iova) { From 03e39005df1eb5a32f0ab0e8175b837b544167d6 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 23 Jan 2015 16:51:21 +0100 Subject: [PATCH 662/788] iommu: exynos: remove useless device_add/remove callbacks The driver doesn't need to do anything important in device add/remove callbacks, because initialization will be done from device-tree specific callbacks added later. IOMMU groups created by current code were never used. Signed-off-by: Marek Szyprowski --- drivers/iommu/exynos-iommu.c | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index e62cb96a742c3a..e40e699423a69d 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -1055,32 +1055,6 @@ static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *domain, return phys; } -static int exynos_iommu_add_device(struct device *dev) -{ - struct iommu_group *group; - int ret; - - group = iommu_group_get(dev); - - if (!group) { - group = iommu_group_alloc(); - if (IS_ERR(group)) { - dev_err(dev, "Failed to allocate IOMMU group\n"); - return PTR_ERR(group); - } - } - - ret = iommu_group_add_device(group, dev); - iommu_group_put(group); - - return ret; -} - -static void exynos_iommu_remove_device(struct device *dev) -{ - iommu_group_remove_device(dev); -} - static const struct iommu_ops exynos_iommu_ops = { .domain_init = exynos_iommu_domain_init, .domain_destroy = exynos_iommu_domain_destroy, @@ -1090,8 +1064,6 @@ static const struct iommu_ops exynos_iommu_ops = { .unmap = exynos_iommu_unmap, .map_sg = default_iommu_map_sg, .iova_to_phys = exynos_iommu_iova_to_phys, - .add_device = exynos_iommu_add_device, - .remove_device = exynos_iommu_remove_device, .pgsize_bitmap = SECT_SIZE | LPAGE_SIZE | SPAGE_SIZE, }; From 8749dea31022436aac8cfbf33a10a4e931c6fd77 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 23 Jan 2015 16:51:22 +0100 Subject: [PATCH 663/788] iommu: exynos: add support for binding more than one sysmmu to master device This patch adds support for assigning more than one SYSMMU controller to the master device. This has been achieved simply by chaning the struct device pointer in struct exynos_iommu_owner into the list of struct sysmmu_drvdata of all controllers assigned to the given master device. Signed-off-by: Marek Szyprowski --- drivers/iommu/exynos-iommu.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index e40e699423a69d..c6cca44d7858ef 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -186,7 +186,7 @@ static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = { /* attached to dev.archdata.iommu of the master device */ struct exynos_iommu_owner { - struct device *sysmmu; + struct list_head clients; }; struct exynos_iommu_domain { @@ -207,6 +207,7 @@ struct sysmmu_drvdata { spinlock_t lock; struct iommu_domain *domain; struct list_head domain_node; + struct list_head owner_node; phys_addr_t pgtable; int version; }; @@ -693,8 +694,7 @@ static int exynos_iommu_attach_device(struct iommu_domain *domain, if (!has_sysmmu(dev)) return -ENODEV; - data = dev_get_drvdata(owner->sysmmu); - if (data) { + list_for_each_entry(data, &owner->clients, owner_node) { ret = __sysmmu_enable(data, pagetable, domain); if (ret >= 0) { data->master = dev; @@ -722,7 +722,7 @@ static void exynos_iommu_detach_device(struct iommu_domain *domain, { struct exynos_iommu_domain *priv = domain->priv; phys_addr_t pagetable = virt_to_phys(priv->pgtable); - struct sysmmu_drvdata *data; + struct sysmmu_drvdata *data, *next; unsigned long flags; int found = 0; @@ -730,14 +730,13 @@ static void exynos_iommu_detach_device(struct iommu_domain *domain, return; spin_lock_irqsave(&priv->lock, flags); - list_for_each_entry(data, &priv->clients, domain_node) { + list_for_each_entry_safe(data, next, &priv->clients, domain_node) { if (data->master == dev) { if (__sysmmu_disable(data)) { data->master = NULL; list_del_init(&data->domain_node); } found = true; - break; } } spin_unlock_irqrestore(&priv->lock, flags); From 33839e5386ba5af7543ea6f9e0866893b3212518 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 23 Jan 2015 16:51:23 +0100 Subject: [PATCH 664/788] iommu: exynos: add support for runtime_pm This patch fixes support for runtime power management for SYSMMU controllers, so they are enabled when master device is attached. Signed-off-by: Marek Szyprowski --- drivers/iommu/exynos-iommu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index c6cca44d7858ef..f8609f40ae59c4 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -695,6 +695,7 @@ static int exynos_iommu_attach_device(struct iommu_domain *domain, return -ENODEV; list_for_each_entry(data, &owner->clients, owner_node) { + pm_runtime_get_sync(data->sysmmu); ret = __sysmmu_enable(data, pagetable, domain); if (ret >= 0) { data->master = dev; @@ -736,6 +737,7 @@ static void exynos_iommu_detach_device(struct iommu_domain *domain, data->master = NULL; list_del_init(&data->domain_node); } + pm_runtime_put(data->sysmmu); found = true; } } From 1eaedc3986162c8c88de80f353c0a57a8f21ce28 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 23 Jan 2015 16:51:24 +0100 Subject: [PATCH 665/788] iommu: exynos: rename variables to reflect their purpose This patch renames some variables to make the code easier to understand. 'domain' is replaced by 'iommu_domain' (more generic entity) and really meaning less 'priv' by 'domain' to reflect its purpose. Signed-off-by: Marek Szyprowski --- drivers/iommu/exynos-iommu.c | 191 ++++++++++++++++++----------------- 1 file changed, 97 insertions(+), 94 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index f8609f40ae59c4..78a12ea78f1a3d 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -430,8 +430,8 @@ static void __sysmmu_enable_nocount(struct sysmmu_drvdata *data) clk_disable(data->clk_master); } -static int __sysmmu_enable(struct sysmmu_drvdata *data, - phys_addr_t pgtable, struct iommu_domain *domain) +static int __sysmmu_enable(struct sysmmu_drvdata *data, phys_addr_t pgtable, + struct iommu_domain *iommu_domain) { int ret = 0; unsigned long flags; @@ -439,7 +439,7 @@ static int __sysmmu_enable(struct sysmmu_drvdata *data, spin_lock_irqsave(&data->lock, flags); if (set_sysmmu_active(data)) { data->pgtable = pgtable; - data->domain = domain; + data->domain = iommu_domain; __sysmmu_enable_nocount(data); @@ -602,92 +602,93 @@ static inline void pgtable_flush(void *vastart, void *vaend) virt_to_phys(vaend)); } -static int exynos_iommu_domain_init(struct iommu_domain *domain) +static int exynos_iommu_domain_init(struct iommu_domain *iommu_domain) { - struct exynos_iommu_domain *priv; + struct exynos_iommu_domain *domain; int i; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) + domain = kzalloc(sizeof(*domain), GFP_KERNEL); + if (!domain) return -ENOMEM; - priv->pgtable = (sysmmu_pte_t *)__get_free_pages(GFP_KERNEL, 2); - if (!priv->pgtable) + domain->pgtable = (sysmmu_pte_t *)__get_free_pages(GFP_KERNEL, 2); + if (!domain->pgtable) goto err_pgtable; - priv->lv2entcnt = (short *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1); - if (!priv->lv2entcnt) + domain->lv2entcnt = (short *) + __get_free_pages(GFP_KERNEL | __GFP_ZERO, 1); + if (!domain->lv2entcnt) goto err_counter; /* Workaround for System MMU v3.3 to prevent caching 1MiB mapping */ for (i = 0; i < NUM_LV1ENTRIES; i += 8) { - priv->pgtable[i + 0] = ZERO_LV2LINK; - priv->pgtable[i + 1] = ZERO_LV2LINK; - priv->pgtable[i + 2] = ZERO_LV2LINK; - priv->pgtable[i + 3] = ZERO_LV2LINK; - priv->pgtable[i + 4] = ZERO_LV2LINK; - priv->pgtable[i + 5] = ZERO_LV2LINK; - priv->pgtable[i + 6] = ZERO_LV2LINK; - priv->pgtable[i + 7] = ZERO_LV2LINK; + domain->pgtable[i + 0] = ZERO_LV2LINK; + domain->pgtable[i + 1] = ZERO_LV2LINK; + domain->pgtable[i + 2] = ZERO_LV2LINK; + domain->pgtable[i + 3] = ZERO_LV2LINK; + domain->pgtable[i + 4] = ZERO_LV2LINK; + domain->pgtable[i + 5] = ZERO_LV2LINK; + domain->pgtable[i + 6] = ZERO_LV2LINK; + domain->pgtable[i + 7] = ZERO_LV2LINK; } - pgtable_flush(priv->pgtable, priv->pgtable + NUM_LV1ENTRIES); + pgtable_flush(domain->pgtable, domain->pgtable + NUM_LV1ENTRIES); - spin_lock_init(&priv->lock); - spin_lock_init(&priv->pgtablelock); - INIT_LIST_HEAD(&priv->clients); + spin_lock_init(&domain->lock); + spin_lock_init(&domain->pgtablelock); + INIT_LIST_HEAD(&domain->clients); - domain->geometry.aperture_start = 0; - domain->geometry.aperture_end = ~0UL; - domain->geometry.force_aperture = true; + iommu_domain->geometry.aperture_start = 0; + iommu_domain->geometry.aperture_end = ~0UL; + iommu_domain->geometry.force_aperture = true; - domain->priv = priv; + iommu_domain->priv = domain; return 0; err_counter: - free_pages((unsigned long)priv->pgtable, 2); + free_pages((unsigned long)domain->pgtable, 2); err_pgtable: - kfree(priv); + kfree(domain); return -ENOMEM; } -static void exynos_iommu_domain_destroy(struct iommu_domain *domain) +static void exynos_iommu_domain_destroy(struct iommu_domain *iommu_domain) { - struct exynos_iommu_domain *priv = domain->priv; + struct exynos_iommu_domain *domain = iommu_domain->priv; struct sysmmu_drvdata *data; unsigned long flags; int i; - WARN_ON(!list_empty(&priv->clients)); + WARN_ON(!list_empty(&domain->clients)); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irqsave(&domain->lock, flags); - list_for_each_entry(data, &priv->clients, domain_node) { + list_for_each_entry(data, &domain->clients, domain_node) { if (__sysmmu_disable(data)) data->master = NULL; list_del_init(&data->domain_node); } - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irqrestore(&domain->lock, flags); for (i = 0; i < NUM_LV1ENTRIES; i++) - if (lv1ent_page(priv->pgtable + i)) + if (lv1ent_page(domain->pgtable + i)) kmem_cache_free(lv2table_kmem_cache, - phys_to_virt(lv2table_base(priv->pgtable + i))); + phys_to_virt(lv2table_base(domain->pgtable + i))); - free_pages((unsigned long)priv->pgtable, 2); - free_pages((unsigned long)priv->lv2entcnt, 1); - kfree(domain->priv); - domain->priv = NULL; + free_pages((unsigned long)domain->pgtable, 2); + free_pages((unsigned long)domain->lv2entcnt, 1); + kfree(iommu_domain->priv); + iommu_domain->priv = NULL; } -static int exynos_iommu_attach_device(struct iommu_domain *domain, +static int exynos_iommu_attach_device(struct iommu_domain *iommu_domain, struct device *dev) { struct exynos_iommu_owner *owner = dev->archdata.iommu; - struct exynos_iommu_domain *priv = domain->priv; + struct exynos_iommu_domain *domain = iommu_domain->priv; struct sysmmu_drvdata *data; - phys_addr_t pagetable = virt_to_phys(priv->pgtable); + phys_addr_t pagetable = virt_to_phys(domain->pgtable); unsigned long flags; int ret = -ENODEV; @@ -696,13 +697,13 @@ static int exynos_iommu_attach_device(struct iommu_domain *domain, list_for_each_entry(data, &owner->clients, owner_node) { pm_runtime_get_sync(data->sysmmu); - ret = __sysmmu_enable(data, pagetable, domain); + ret = __sysmmu_enable(data, pagetable, iommu_domain); if (ret >= 0) { data->master = dev; - spin_lock_irqsave(&priv->lock, flags); - list_add_tail(&data->domain_node, &priv->clients); - spin_unlock_irqrestore(&priv->lock, flags); + spin_lock_irqsave(&domain->lock, flags); + list_add_tail(&data->domain_node, &domain->clients); + spin_unlock_irqrestore(&domain->lock, flags); } } @@ -718,11 +719,11 @@ static int exynos_iommu_attach_device(struct iommu_domain *domain, return ret; } -static void exynos_iommu_detach_device(struct iommu_domain *domain, +static void exynos_iommu_detach_device(struct iommu_domain *iommu_domain, struct device *dev) { - struct exynos_iommu_domain *priv = domain->priv; - phys_addr_t pagetable = virt_to_phys(priv->pgtable); + struct exynos_iommu_domain *domain = iommu_domain->priv; + phys_addr_t pagetable = virt_to_phys(domain->pgtable); struct sysmmu_drvdata *data, *next; unsigned long flags; int found = 0; @@ -730,8 +731,8 @@ static void exynos_iommu_detach_device(struct iommu_domain *domain, if (!has_sysmmu(dev)) return; - spin_lock_irqsave(&priv->lock, flags); - list_for_each_entry_safe(data, next, &priv->clients, domain_node) { + spin_lock_irqsave(&domain->lock, flags); + list_for_each_entry_safe(data, next, &domain->clients, domain_node) { if (data->master == dev) { if (__sysmmu_disable(data)) { data->master = NULL; @@ -741,7 +742,7 @@ static void exynos_iommu_detach_device(struct iommu_domain *domain, found = true; } } - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irqrestore(&domain->lock, flags); if (found) dev_dbg(dev, "%s: Detached IOMMU with pgtable %pa\n", @@ -750,7 +751,7 @@ static void exynos_iommu_detach_device(struct iommu_domain *domain, dev_err(dev, "%s: No IOMMU is attached\n", __func__); } -static sysmmu_pte_t *alloc_lv2entry(struct exynos_iommu_domain *priv, +static sysmmu_pte_t *alloc_lv2entry(struct exynos_iommu_domain *domain, sysmmu_pte_t *sent, sysmmu_iova_t iova, short *pgcounter) { if (lv1ent_section(sent)) { @@ -792,17 +793,17 @@ static sysmmu_pte_t *alloc_lv2entry(struct exynos_iommu_domain *priv, if (need_flush_flpd_cache) { struct sysmmu_drvdata *data; - spin_lock(&priv->lock); - list_for_each_entry(data, &priv->clients, domain_node) + spin_lock(&domain->lock); + list_for_each_entry(data, &domain->clients, domain_node) sysmmu_tlb_invalidate_flpdcache(data, iova); - spin_unlock(&priv->lock); + spin_unlock(&domain->lock); } } return page_entry(sent, iova); } -static int lv1set_section(struct exynos_iommu_domain *priv, +static int lv1set_section(struct exynos_iommu_domain *domain, sysmmu_pte_t *sent, sysmmu_iova_t iova, phys_addr_t paddr, short *pgcnt) { @@ -827,17 +828,17 @@ static int lv1set_section(struct exynos_iommu_domain *priv, pgtable_flush(sent, sent + 1); - spin_lock(&priv->lock); + spin_lock(&domain->lock); if (lv1ent_page_zero(sent)) { struct sysmmu_drvdata *data; /* * Flushing FLPD cache in System MMU v3.3 that may cache a FLPD * entry by speculative prefetch of SLPD which has no mapping. */ - list_for_each_entry(data, &priv->clients, domain_node) + list_for_each_entry(data, &domain->clients, domain_node) sysmmu_tlb_invalidate_flpdcache(data, iova); } - spin_unlock(&priv->lock); + spin_unlock(&domain->lock); return 0; } @@ -897,74 +898,76 @@ static int lv2set_page(sysmmu_pte_t *pent, phys_addr_t paddr, size_t size, * than or equal to 128KiB. * - Start address of an I/O virtual region must be aligned by 128KiB. */ -static int exynos_iommu_map(struct iommu_domain *domain, unsigned long l_iova, - phys_addr_t paddr, size_t size, int prot) +static int exynos_iommu_map(struct iommu_domain *iommu_domain, + unsigned long l_iova, phys_addr_t paddr, size_t size, + int prot) { - struct exynos_iommu_domain *priv = domain->priv; + struct exynos_iommu_domain *domain = iommu_domain->priv; sysmmu_pte_t *entry; sysmmu_iova_t iova = (sysmmu_iova_t)l_iova; unsigned long flags; int ret = -ENOMEM; - BUG_ON(priv->pgtable == NULL); + BUG_ON(domain->pgtable == NULL); - spin_lock_irqsave(&priv->pgtablelock, flags); + spin_lock_irqsave(&domain->pgtablelock, flags); - entry = section_entry(priv->pgtable, iova); + entry = section_entry(domain->pgtable, iova); if (size == SECT_SIZE) { - ret = lv1set_section(priv, entry, iova, paddr, - &priv->lv2entcnt[lv1ent_offset(iova)]); + ret = lv1set_section(domain, entry, iova, paddr, + &domain->lv2entcnt[lv1ent_offset(iova)]); } else { sysmmu_pte_t *pent; - pent = alloc_lv2entry(priv, entry, iova, - &priv->lv2entcnt[lv1ent_offset(iova)]); + pent = alloc_lv2entry(domain, entry, iova, + &domain->lv2entcnt[lv1ent_offset(iova)]); if (IS_ERR(pent)) ret = PTR_ERR(pent); else ret = lv2set_page(pent, paddr, size, - &priv->lv2entcnt[lv1ent_offset(iova)]); + &domain->lv2entcnt[lv1ent_offset(iova)]); } if (ret) pr_err("%s: Failed(%d) to map %#zx bytes @ %#x\n", __func__, ret, size, iova); - spin_unlock_irqrestore(&priv->pgtablelock, flags); + spin_unlock_irqrestore(&domain->pgtablelock, flags); return ret; } -static void exynos_iommu_tlb_invalidate_entry(struct exynos_iommu_domain *priv, - sysmmu_iova_t iova, size_t size) +static +void exynos_iommu_tlb_invalidate_entry(struct exynos_iommu_domain *domain, + sysmmu_iova_t iova, size_t size) { struct sysmmu_drvdata *data; unsigned long flags; - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irqsave(&domain->lock, flags); - list_for_each_entry(data, &priv->clients, domain_node) + list_for_each_entry(data, &domain->clients, domain_node) sysmmu_tlb_invalidate_entry(data, iova, size); - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irqrestore(&domain->lock, flags); } -static size_t exynos_iommu_unmap(struct iommu_domain *domain, +static size_t exynos_iommu_unmap(struct iommu_domain *iommu_domain, unsigned long l_iova, size_t size) { - struct exynos_iommu_domain *priv = domain->priv; + struct exynos_iommu_domain *domain = iommu_domain->priv; sysmmu_iova_t iova = (sysmmu_iova_t)l_iova; sysmmu_pte_t *ent; size_t err_pgsize; unsigned long flags; - BUG_ON(priv->pgtable == NULL); + BUG_ON(domain->pgtable == NULL); - spin_lock_irqsave(&priv->pgtablelock, flags); + spin_lock_irqsave(&domain->pgtablelock, flags); - ent = section_entry(priv->pgtable, iova); + ent = section_entry(domain->pgtable, iova); if (lv1ent_section(ent)) { if (WARN_ON(size < SECT_SIZE)) { @@ -998,7 +1001,7 @@ static size_t exynos_iommu_unmap(struct iommu_domain *domain, *ent = 0; size = SPAGE_SIZE; pgtable_flush(ent, ent + 1); - priv->lv2entcnt[lv1ent_offset(iova)] += 1; + domain->lv2entcnt[lv1ent_offset(iova)] += 1; goto done; } @@ -1012,15 +1015,15 @@ static size_t exynos_iommu_unmap(struct iommu_domain *domain, pgtable_flush(ent, ent + SPAGES_PER_LPAGE); size = LPAGE_SIZE; - priv->lv2entcnt[lv1ent_offset(iova)] += SPAGES_PER_LPAGE; + domain->lv2entcnt[lv1ent_offset(iova)] += SPAGES_PER_LPAGE; done: - spin_unlock_irqrestore(&priv->pgtablelock, flags); + spin_unlock_irqrestore(&domain->pgtablelock, flags); - exynos_iommu_tlb_invalidate_entry(priv, iova, size); + exynos_iommu_tlb_invalidate_entry(domain, iova, size); return size; err: - spin_unlock_irqrestore(&priv->pgtablelock, flags); + spin_unlock_irqrestore(&domain->pgtablelock, flags); pr_err("%s: Failed: size(%#zx) @ %#x is smaller than page size %#zx\n", __func__, size, iova, err_pgsize); @@ -1028,17 +1031,17 @@ static size_t exynos_iommu_unmap(struct iommu_domain *domain, return 0; } -static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *domain, +static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *iommu_domain, dma_addr_t iova) { - struct exynos_iommu_domain *priv = domain->priv; + struct exynos_iommu_domain *domain = iommu_domain->priv; sysmmu_pte_t *entry; unsigned long flags; phys_addr_t phys = 0; - spin_lock_irqsave(&priv->pgtablelock, flags); + spin_lock_irqsave(&domain->pgtablelock, flags); - entry = section_entry(priv->pgtable, iova); + entry = section_entry(domain->pgtable, iova); if (lv1ent_section(entry)) { phys = section_phys(entry) + section_offs(iova); @@ -1051,7 +1054,7 @@ static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *domain, phys = spage_phys(entry) + spage_offs(iova); } - spin_unlock_irqrestore(&priv->pgtablelock, flags); + spin_unlock_irqrestore(&domain->pgtablelock, flags); return phys; } From 78b0c2e6840dfcaa025e32692fa5186e55060860 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 23 Jan 2015 16:51:25 +0100 Subject: [PATCH 666/788] iommu: exynos: document internal structures Add a few words of comment to all internal structures used by the driver. Signed-off-by: Marek Szyprowski --- drivers/iommu/exynos-iommu.c | 49 ++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 78a12ea78f1a3d..ef7172551a3952 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -184,32 +184,49 @@ static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = { "UNKNOWN FAULT" }; -/* attached to dev.archdata.iommu of the master device */ +/* + * This structure is attached to dev.archdata.iommu of the master device + * on device add, contains a list of SYSMMU controllers defined by device tree, + * which are bound to given master device. It is usually referenced by 'owner' + * pointer. + */ struct exynos_iommu_owner { - struct list_head clients; + struct list_head clients; /* list of sysmmu_drvdata.owner_node */ }; +/* + * This structure is stored in ->priv field of generic struct iommu_domain, + * contains list of SYSMMU controllers from all master devices, which has been + * attached to this domain and page tables of IO address space defined by this + * domain. It is usually referenced by 'domain' pointer. + */ struct exynos_iommu_domain { - struct list_head clients; /* list of sysmmu_drvdata.node */ + struct list_head clients; /* list of sysmmu_drvdata.domain_node */ sysmmu_pte_t *pgtable; /* lv1 page table, 16KB */ short *lv2entcnt; /* free lv2 entry counter for each section */ - spinlock_t lock; /* lock for this structure */ + spinlock_t lock; /* lock for modyfying list of clients */ spinlock_t pgtablelock; /* lock for modifying page table @ pgtable */ }; +/* + * This structure hold all data of a single SYSMMU controller, this includes + * hw resources like registers and clocks, pointers and list nodes to connect + * it to all other structures, internal state and parameters read from device + * tree. It is usually referenced by 'data' pointer. + */ struct sysmmu_drvdata { - struct device *sysmmu; /* System MMU's device descriptor */ - struct device *master; /* Owner of system MMU */ - void __iomem *sfrbase; - struct clk *clk; - struct clk *clk_master; - int activations; - spinlock_t lock; - struct iommu_domain *domain; - struct list_head domain_node; - struct list_head owner_node; - phys_addr_t pgtable; - int version; + struct device *sysmmu; /* SYSMMU controller device */ + struct device *master; /* master device (owner of given SYSMMU) */ + void __iomem *sfrbase; /* our registers */ + struct clk *clk; /* SYSMMU's clock */ + struct clk *clk_master; /* master's device clock */ + int activations; /* number of calls to sysmmu_enable */ + spinlock_t lock; /* lock for modyfying enable/disable state */ + struct iommu_domain *domain; /* domain we belong to */ + struct list_head domain_node; /* node for domain clients list */ + struct list_head owner_node; /* node for owner clients list */ + phys_addr_t pgtable; /* assigned page table structure */ + int version; /* our version */ }; static bool set_sysmmu_active(struct sysmmu_drvdata *data) From 6f2a4383609ca202285f7bb1e4ee52c73e3bd8bf Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 23 Jan 2015 16:51:26 +0100 Subject: [PATCH 667/788] iommu: exynos: remove excessive includes and sort others alphabetically Removed following unused includes: , , and . Signed-off-by: Marek Szyprowski --- drivers/iommu/exynos-iommu.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index ef7172551a3952..c53cc8f61176cc 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -12,19 +12,15 @@ #define DEBUG #endif -#include -#include -#include -#include -#include #include #include -#include +#include #include -#include +#include #include -#include -#include +#include +#include +#include #include #include From de3a9310e7fa2527dbb62ec9e6d52dfe9456baea Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 23 Jan 2015 16:51:27 +0100 Subject: [PATCH 668/788] iommu: exynos: init from dt-specific callback instead of initcall This patch introduces IOMMU_OF_DECLARE-based initialization to the driver, which replaces subsys_initcall-based procedure. exynos_iommu_of_setup ensures that each sysmmu controller is probed before its master device. Signed-off-by: Marek Szyprowski --- drivers/iommu/exynos-iommu.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index c53cc8f61176cc..ff169cee34474d 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -13,16 +13,21 @@ #endif #include +#include #include #include #include #include #include +#include +#include +#include #include #include #include #include +#include #include typedef u32 sysmmu_iova_t; @@ -1072,7 +1077,7 @@ static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *iommu_domain, return phys; } -static const struct iommu_ops exynos_iommu_ops = { +static struct iommu_ops exynos_iommu_ops = { .domain_init = exynos_iommu_domain_init, .domain_destroy = exynos_iommu_domain_destroy, .attach_dev = exynos_iommu_attach_device, @@ -1084,6 +1089,8 @@ static const struct iommu_ops exynos_iommu_ops = { .pgsize_bitmap = SECT_SIZE | LPAGE_SIZE | SPAGE_SIZE, }; +static int init_done; + static int __init exynos_iommu_init(void) { int ret; @@ -1116,6 +1123,8 @@ static int __init exynos_iommu_init(void) goto err_set_iommu; } + init_done = true; + return 0; err_set_iommu: kmem_cache_free(lv2table_kmem_cache, zero_lv2_table); @@ -1125,4 +1134,21 @@ static int __init exynos_iommu_init(void) kmem_cache_destroy(lv2table_kmem_cache); return ret; } -subsys_initcall(exynos_iommu_init); + +static int __init exynos_iommu_of_setup(struct device_node *np) +{ + struct platform_device *pdev; + + if (!init_done) + exynos_iommu_init(); + + pdev = of_platform_device_create(np, NULL, platform_bus_type.dev_root); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); + + of_iommu_set_ops(np, &exynos_iommu_ops); + return 0; +} + +IOMMU_OF_DECLARE(exynos_iommu_of, "samsung,exynos-sysmmu", + exynos_iommu_of_setup); From 81deee454f93ad6296e5b00d3f2ac34c7eb03874 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 23 Jan 2015 16:51:28 +0100 Subject: [PATCH 669/788] iommu: exynos: add callback for initializing devices from device tree This patch adds implementation of of_xlate callback, which prepares masters device for attaching to IOMMU. This callback is called during creating devices from device tree. Signed-off-by: Marek Szyprowski --- drivers/iommu/exynos-iommu.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index ff169cee34474d..99217f75142cec 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -1077,6 +1077,33 @@ static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *iommu_domain, return phys; } +static int exynos_iommu_of_xlate(struct device *dev, + struct of_phandle_args *spec) +{ + struct exynos_iommu_owner *owner = dev->archdata.iommu; + struct platform_device *sysmmu = of_find_device_by_node(spec->np); + struct sysmmu_drvdata *data; + + if (!sysmmu) + return -ENODEV; + + data = platform_get_drvdata(sysmmu); + if (!data) + return -ENODEV; + + if (!owner) { + owner = kzalloc(sizeof(*owner), GFP_KERNEL); + if (!owner) + return -ENOMEM; + + INIT_LIST_HEAD(&owner->clients); + dev->archdata.iommu = owner; + } + + list_add_tail(&data->owner_node, &owner->clients); + return 0; +} + static struct iommu_ops exynos_iommu_ops = { .domain_init = exynos_iommu_domain_init, .domain_destroy = exynos_iommu_domain_destroy, @@ -1087,6 +1114,7 @@ static struct iommu_ops exynos_iommu_ops = { .map_sg = default_iommu_map_sg, .iova_to_phys = exynos_iommu_iova_to_phys, .pgsize_bitmap = SECT_SIZE | LPAGE_SIZE | SPAGE_SIZE, + .of_xlate = exynos_iommu_of_xlate, }; static int init_done; From 89ac350d924b778ad3816941efd0f4b3b7ff836c Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sat, 17 Jan 2015 02:04:45 +0100 Subject: [PATCH 670/788] patchset: [PATCH v5 00/18] Exynos SYSMMU (IOMMU) integration with DT and DMA-mapping subsystem --- PATCHES | 1 + 1 file changed, 1 insertion(+) diff --git a/PATCHES b/PATCHES index b650bc5f50a75d..6aa73cf3890b5c 100644 --- a/PATCHES +++ b/PATCHES @@ -3,3 +3,4 @@ [PATCH V3 00/15] ASoC: samsung: Add clk provider for I2S internal clocks [PATCHv3 0/8] devfreq: Add generic exynos memory-bus frequency driver [PATCH v6 00/18] thermal: exynos: Thermal code rework to use device tree +[PATCH v5 00/18] Exynos SYSMMU (IOMMU) integration with DT and DMA-mapping subsystem From 5e9174fd4708a1b44620282bd26ebe5df533343f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 3 Feb 2015 18:28:56 +0100 Subject: [PATCH 671/788] ARM: EXYNOS: Fix failed second suspend on Exynos4 On Exynos4412 boards (Trats2, Odroid U3) after enabling L2 cache in 56b60b8bce4a ("ARM: 8265/1: dts: exynos4: Add nodes for L2 cache controller") the second suspend to RAM failed. First suspend worked fine but the next one hang just after powering down of secondary CPUs (system consumed energy as it would be running but was not responsive). The issue was caused by enabling delayed reset assertion for CPU0 just after issuing power down of cores. This was introduced for Exynos4 in 13cfa6c4f7fa ("ARM: EXYNOS: Fix CPU idle clock down after CPU off"). The whole behavior is not well documented but after checking with vendor code this should be done like this (on Exynos4): 1. Enable delayed reset assertion when system is running (for all CPUs). 2. Disable delayed reset assertion before suspending the system. This can be done after powering off secondary CPUs. 3. Re-enable the delayed reset assertion when system is resumed. Signed-off-by: Krzysztof Kozlowski --- arch/arm/mach-exynos/common.h | 2 ++ arch/arm/mach-exynos/exynos.c | 27 +++++++++++++++++++++++ arch/arm/mach-exynos/platsmp.c | 39 ++-------------------------------- arch/arm/mach-exynos/suspend.c | 3 +++ 4 files changed, 34 insertions(+), 37 deletions(-) diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h index 865f878063cc12..738f12c0489700 100644 --- a/arch/arm/mach-exynos/common.h +++ b/arch/arm/mach-exynos/common.h @@ -150,6 +150,8 @@ extern void exynos_pm_central_suspend(void); extern int exynos_pm_central_resume(void); extern void exynos_enter_aftr(void); +extern void exynos_set_delayed_reset_assertion(bool enable); + extern void s5p_init_cpu(void __iomem *cpuid_addr); extern unsigned int samsung_rev(void); diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c index c13d0837fa8cd3..c78d631d78a7ed 100644 --- a/arch/arm/mach-exynos/exynos.c +++ b/arch/arm/mach-exynos/exynos.c @@ -175,6 +175,33 @@ static void __init exynos_init_io(void) exynos_map_io(); } +/* + * Set or clear the USE_DELAYED_RESET_ASSERTION option. Used by smp code + * and suspend. + * + * This is necessary only on Exynos4 SoCs. When system is running + * USE_DELAYED_RESET_ASSERTION should be set so the ARM CLK clock down + * feature could properly detect global idle state when secondary CPU is + * powered down. + * + * However this should not be set when such system is going into suspend. + */ +void exynos_set_delayed_reset_assertion(bool enable) +{ + if (soc_is_exynos4()) { + unsigned int tmp, core_id; + + for (core_id = 0; core_id < num_possible_cpus(); core_id++) { + tmp = pmu_raw_readl(EXYNOS_ARM_CORE_OPTION(core_id)); + if (enable) + tmp |= S5P_USE_DELAYED_RESET_ASSERTION; + else + tmp &= ~(S5P_USE_DELAYED_RESET_ASSERTION); + pmu_raw_writel(tmp, EXYNOS_ARM_CORE_OPTION(core_id)); + } + } +} + static const struct of_device_id exynos_dt_pmu_match[] = { { .compatible = "samsung,exynos3250-pmu" }, { .compatible = "samsung,exynos4210-pmu" }, diff --git a/arch/arm/mach-exynos/platsmp.c b/arch/arm/mach-exynos/platsmp.c index 0abb7dff73ab8f..1fd3bb48ad6c7d 100644 --- a/arch/arm/mach-exynos/platsmp.c +++ b/arch/arm/mach-exynos/platsmp.c @@ -34,30 +34,6 @@ extern void exynos4_secondary_startup(void); -/* - * Set or clear the USE_DELAYED_RESET_ASSERTION option, set on Exynos4 SoCs - * during hot-(un)plugging CPUx. - * - * The feature can be cleared safely during first boot of secondary CPU. - * - * Exynos4 SoCs require setting USE_DELAYED_RESET_ASSERTION during powering - * down a CPU so the CPU idle clock down feature could properly detect global - * idle state when CPUx is off. - */ -static void exynos_set_delayed_reset_assertion(u32 core_id, bool enable) -{ - if (soc_is_exynos4()) { - unsigned int tmp; - - tmp = pmu_raw_readl(EXYNOS_ARM_CORE_OPTION(core_id)); - if (enable) - tmp |= S5P_USE_DELAYED_RESET_ASSERTION; - else - tmp &= ~(S5P_USE_DELAYED_RESET_ASSERTION); - pmu_raw_writel(tmp, EXYNOS_ARM_CORE_OPTION(core_id)); - } -} - #ifdef CONFIG_HOTPLUG_CPU static inline void cpu_leave_lowpower(u32 core_id) { @@ -73,8 +49,6 @@ static inline void cpu_leave_lowpower(u32 core_id) : "=&r" (v) : "Ir" (CR_C), "Ir" (0x40) : "cc"); - - exynos_set_delayed_reset_assertion(core_id, false); } static inline void platform_do_lowpower(unsigned int cpu, int *spurious) @@ -87,14 +61,6 @@ static inline void platform_do_lowpower(unsigned int cpu, int *spurious) /* Turn the CPU off on next WFI instruction. */ exynos_cpu_power_down(core_id); - /* - * Exynos4 SoCs require setting - * USE_DELAYED_RESET_ASSERTION so the CPU idle - * clock down feature could properly detect - * global idle state when CPUx is off. - */ - exynos_set_delayed_reset_assertion(core_id, true); - wfi(); if (pen_release == core_id) { @@ -354,9 +320,6 @@ static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle) udelay(10); } - /* No harm if this is called during first boot of secondary CPU */ - exynos_set_delayed_reset_assertion(core_id, false); - /* * now the secondary core is starting up let it run its * calibrations, then wait for it to finish @@ -403,6 +366,8 @@ static void __init exynos_smp_prepare_cpus(unsigned int max_cpus) exynos_sysram_init(); + exynos_set_delayed_reset_assertion(true); + if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) scu_enable(scu_base_addr()); diff --git a/arch/arm/mach-exynos/suspend.c b/arch/arm/mach-exynos/suspend.c index f8e7dcd1705529..7f729048aaa935 100644 --- a/arch/arm/mach-exynos/suspend.c +++ b/arch/arm/mach-exynos/suspend.c @@ -215,6 +215,8 @@ static void exynos_pm_enter_sleep_mode(void) static void exynos_pm_prepare(void) { + exynos_set_delayed_reset_assertion(false); + /* Set wake-up mask registers */ exynos_pm_set_wakeup_mask(); @@ -342,6 +344,7 @@ static void exynos_pm_resume(void) /* Clear SLEEP mode set in INFORM1 */ pmu_raw_writel(0x0, S5P_INFORM1); + exynos_set_delayed_reset_assertion(true); } static void exynos5420_prepare_pm_resume(void) From d9ba082928abccbdbb615c5cdb1f7d11b1460593 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 3 Feb 2015 18:28:49 +0100 Subject: [PATCH 672/788] ARM: EXYNOS: Handle of_find_device_by_node and kstrdup failures Prevent possible NULL pointer dereference of pointer returned by of_find_device_by_node(). Handle this by skipping such power domain. Additionally fail the init on kstrdup() failure. Such case is actually not fatal because the name for power domain allocated by kstrdup() is used only in printk. Still as a precaution handle this as an error condition. Signed-off-by: Krzysztof Kozlowski --- arch/arm/mach-exynos/pm_domains.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/arm/mach-exynos/pm_domains.c b/arch/arm/mach-exynos/pm_domains.c index 0e2bc366b5eb6d..0bc43815ae52d9 100644 --- a/arch/arm/mach-exynos/pm_domains.c +++ b/arch/arm/mach-exynos/pm_domains.c @@ -122,6 +122,12 @@ static __init int exynos4_pm_init_power_domain(void) struct device *dev; pdev = of_find_device_by_node(np); + if (!pdev) { + pr_err("%s: failed to find device for node %s\n", + __func__, np->name); + of_node_put(np); + continue; + } dev = &pdev->dev; pd = kzalloc(sizeof(*pd), GFP_KERNEL); @@ -132,6 +138,12 @@ static __init int exynos4_pm_init_power_domain(void) } pd->pd.name = kstrdup(np->name, GFP_KERNEL); + if (!pd->pd.name) { + kfree(pd); + of_node_put(np); + return -ENOMEM; + } + pd->name = pd->pd.name; pd->base = of_iomap(np, 0); pd->pd.power_off = exynos_pd_power_off; From da22babf3c31f3653b0ef90e0395ec62fae33e5b Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 20 Jan 2015 15:31:14 +0100 Subject: [PATCH 673/788] drm/exynos: IOMMU support should not be selectable by user If system provides IOMMU feature, Exynos DRM should use it by default, because the Exynos DRM subdrivers don't work correctly when Exynos IOMMU driver has been enabled and no IOMMU support has been compiled into Exynos DRM driver. Signed-off-by: Marek Szyprowski --- drivers/gpu/drm/exynos/Kconfig | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 7f9f6f9e9b7e79..39fe490efcd4ba 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -13,10 +13,9 @@ config DRM_EXYNOS If M is selected the module will be called exynosdrm. config DRM_EXYNOS_IOMMU - bool "EXYNOS DRM IOMMU Support" + bool depends on DRM_EXYNOS && EXYNOS_IOMMU && ARM_DMA_USE_IOMMU - help - Choose this option if you want to use IOMMU feature for DRM. + default y config DRM_EXYNOS_DMABUF bool "EXYNOS DRM DMABUF" From 1edc7ea1d7ab0925c06dc4e5a273aad4c0253b8a Mon Sep 17 00:00:00 2001 From: Sjoerd Simons Date: Thu, 22 Jan 2015 22:41:33 +0100 Subject: [PATCH 674/788] pwm: samsung: Fix output race on disabling When disabling the samsung PWM the output state remains at the level it was in the end of a pwm cycle. In other words, calling pwm_disable when at 100% duty will keep the output active, while at all other setting the output will go/stay inactive. On top of that the samsung PWM settings are double-buffered, which means the new settings only get applied at the start of a new PWM cycle. This results in a race if the PWM is at 100% duty and a driver calls: pwm_config (pwm, 0, period); pwm_disable (pwm); In this case the PWMs output will unexpectedly stay active, unless a new PWM cycle happened to start between the register writes in _config and _disable. As far as i can tell this is a regression introduced by 3bdf878, before that a call to pwm_config would call pwm_samsung_enable which, while heavy-handed, made sure the expected settings were live. To resolve this, while not re-introducing the issues 3bdf878 (flickering as the PWM got reset while in a PWM cycle). Only force an update of the settings when at 100% duty, which shouldn't have a noticeable effect on the output but is enough to ensure the behaviour is as expected on disable. Signed-off-by: Sjoerd Simons --- drivers/pwm/pwm-samsung.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c index 3e9b5835a4af80..649f6c424c3137 100644 --- a/drivers/pwm/pwm-samsung.c +++ b/drivers/pwm/pwm-samsung.c @@ -269,12 +269,31 @@ static void pwm_samsung_disable(struct pwm_chip *chip, struct pwm_device *pwm) spin_unlock_irqrestore(&samsung_pwm_lock, flags); } +static void pwm_samsung_manual_update(struct samsung_pwm_chip *chip, + struct pwm_device *pwm) +{ + unsigned int tcon_chan = to_tcon_channel(pwm->hwpwm); + u32 tcon; + unsigned long flags; + + spin_lock_irqsave(&samsung_pwm_lock, flags); + + tcon = readl(chip->base + REG_TCON); + tcon |= TCON_MANUALUPDATE(tcon_chan); + writel(tcon, chip->base + REG_TCON); + + tcon &= ~TCON_MANUALUPDATE(tcon_chan); + writel(tcon, chip->base + REG_TCON); + + spin_unlock_irqrestore(&samsung_pwm_lock, flags); +} + static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) { struct samsung_pwm_chip *our_chip = to_samsung_pwm_chip(chip); struct samsung_pwm_channel *chan = pwm_get_chip_data(pwm); - u32 tin_ns = chan->tin_ns, tcnt, tcmp; + u32 tin_ns = chan->tin_ns, tcnt, tcmp, oldtcmp; /* * We currently avoid using 64bit arithmetic by using the @@ -288,6 +307,7 @@ static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, return 0; tcnt = readl(our_chip->base + REG_TCNTB(pwm->hwpwm)); + oldtcmp = readl(our_chip->base + REG_TCMPB(pwm->hwpwm)); /* We need tick count for calculation, not last tick. */ ++tcnt; @@ -335,6 +355,15 @@ static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, writel(tcnt, our_chip->base + REG_TCNTB(pwm->hwpwm)); writel(tcmp, our_chip->base + REG_TCMPB(pwm->hwpwm)); + /* In case the PWM is currently at 100% duty, force a manual update + * to prevent the signal staying high in the pwm is disabled shortly + * afer this update (before it autoreloaded the new values) . + */ + if (oldtcmp == (u32) -1) { + dev_dbg(our_chip->chip.dev, "Forcing manual update"); + pwm_samsung_manual_update(our_chip, pwm); + } + chan->period_ns = period_ns; chan->tin_ns = tin_ns; chan->duty_ns = duty_ns; From 8d9a72d400b0592e5b1dd7a9aba113c33162f667 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Fri, 23 Jan 2015 14:47:41 +0100 Subject: [PATCH 675/788] serial: samsung: Add support for early console This patch adds support for early console initialized from device tree and kernel command line to all variants of Samsung serial driver. Signed-off-by: Tomasz Figa [mszyprow: added support for command line based initialization, fixed comments, added documentation] Signed-off-by: Marek Szyprowski Reviewed-by: Alim Akhtar Tested-by: Alim Akhtar Tested-by: Chanwoo Choi --- Documentation/kernel-parameters.txt | 12 ++++ drivers/tty/serial/Kconfig | 1 + drivers/tty/serial/samsung.c | 103 ++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+) diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 176d4fe4f076be..98f133ce7f8672 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -970,6 +970,18 @@ bytes respectively. Such letter suffixes can also be entirely omitted. smh Use ARM semihosting calls for early console. + s3c2410, + s3c2412, + s3c2440, + s3c6400, + s5pv210, + exynos4210, + Use early console provided by serial driver available + on Samsung SoCs, requires selecting proper type and + a correct base address of the selected UART port. The + serial port must already be setup and configured. + Options are not yet supported. + earlyprintk= [X86,SH,BLACKFIN,ARM,M68k] earlyprintk=vga earlyprintk=efi diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index c79b43cd6014a9..90c3848b49d1d1 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -241,6 +241,7 @@ config SERIAL_SAMSUNG tristate "Samsung SoC serial support" depends on PLAT_SAMSUNG || ARCH_EXYNOS select SERIAL_CORE + select SERIAL_EARLYCON help Support for the on-chip UARTs on the Samsung S3C24XX series CPUs, providing /dev/ttySAC0, 1 and 2 (note, some machines may not diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 107e8072257526..998b956ec27e28 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -1857,6 +1857,109 @@ static struct platform_driver samsung_serial_driver = { module_platform_driver(samsung_serial_driver); +/* + * Early console. + */ + +struct samsung_early_console_data { + u32 txfull_mask; +}; + +static void samsung_early_busyuart(struct uart_port *port) +{ + while (!(readl(port->membase + S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXFE)) + ; +} + +static void samsung_early_busyuart_fifo(struct uart_port *port) +{ + struct samsung_early_console_data *data = port->private_data; + + while (readl(port->membase + S3C2410_UFSTAT) & data->txfull_mask) + ; +} + +static void samsung_early_putc(struct uart_port *port, int c) +{ + if (readl(port->membase + S3C2410_UFCON) & S3C2410_UFCON_FIFOMODE) + samsung_early_busyuart_fifo(port); + else + samsung_early_busyuart(port); + + writeb(c, port->membase + S3C2410_UTXH); +} + +static void samsung_early_write(struct console *con, const char *s, unsigned n) +{ + struct earlycon_device *dev = con->data; + + uart_console_write(&dev->port, s, n, samsung_early_putc); +} + +static int __init samsung_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + device->con->write = samsung_early_write; + return 0; +} + +/* S3C2410 */ +static struct samsung_early_console_data s3c2410_early_console_data = { + .txfull_mask = S3C2410_UFSTAT_TXFULL, +}; + +static int __init s3c2410_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + device->port.private_data = &s3c2410_early_console_data; + return samsung_early_console_setup(device, opt); +} +OF_EARLYCON_DECLARE(s3c2410, "samsung,s3c2410-uart", + s3c2410_early_console_setup); +EARLYCON_DECLARE(s3c2410, s3c2410_early_console_setup); + +/* S3C2412, S3C2440, S3C64xx */ +static struct samsung_early_console_data s3c2440_early_console_data = { + .txfull_mask = S3C2440_UFSTAT_TXFULL, +}; + +static int __init s3c2440_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + device->port.private_data = &s3c2440_early_console_data; + return samsung_early_console_setup(device, opt); +} +OF_EARLYCON_DECLARE(s3c2412, "samsung,s3c2412-uart", + s3c2440_early_console_setup); +OF_EARLYCON_DECLARE(s3c2440, "samsung,s3c2440-uart", + s3c2440_early_console_setup); +OF_EARLYCON_DECLARE(s3c6400, "samsung,s3c6400-uart", + s3c2440_early_console_setup); +EARLYCON_DECLARE(s3c2412, s3c2440_early_console_setup); +EARLYCON_DECLARE(s3c2440, s3c2440_early_console_setup); +EARLYCON_DECLARE(s3c6400, s3c2440_early_console_setup); + +/* S5PV210, EXYNOS */ +static struct samsung_early_console_data s5pv210_early_console_data = { + .txfull_mask = S5PV210_UFSTAT_TXFULL, +}; + +static int __init s5pv210_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + device->port.private_data = &s5pv210_early_console_data; + return samsung_early_console_setup(device, opt); +} +OF_EARLYCON_DECLARE(s5pv210, "samsung,s5pv210-uart", + s5pv210_early_console_setup); +OF_EARLYCON_DECLARE(exynos4210, "samsung,exynos4210-uart", + s5pv210_early_console_setup); +EARLYCON_DECLARE(s5pv210, s5pv210_early_console_setup); +EARLYCON_DECLARE(exynos4210, s5pv210_early_console_setup); + MODULE_ALIAS("platform:samsung-uart"); MODULE_DESCRIPTION("Samsung SoC Serial port driver"); MODULE_AUTHOR("Ben Dooks "); From 1c570f5c610a2375b182de101c9f90dee01b5b05 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Fri, 23 Jan 2015 14:47:42 +0100 Subject: [PATCH 676/788] ARM: dts: exynos4: Add stdout-path properties This patch adds stdout-path property to chosen nodes of Exynos4 boards to enable use of earlycon feature without the need to hardcode port number in kernel itself. Signed-off-by: Tomasz Figa Signed-off-by: Marek Szyprowski --- arch/arm/boot/dts/exynos4210-origen.dts | 1 + arch/arm/boot/dts/exynos4210-smdkv310.dts | 1 + arch/arm/boot/dts/exynos4210-trats.dts | 1 + arch/arm/boot/dts/exynos4210-universal_c210.dts | 1 + arch/arm/boot/dts/exynos4412-odroid-common.dtsi | 4 ++++ arch/arm/boot/dts/exynos4412-origen.dts | 1 + arch/arm/boot/dts/exynos4412-smdk4412.dts | 1 + arch/arm/boot/dts/exynos4412-tiny4412.dts | 4 ++++ arch/arm/boot/dts/exynos4412-trats2.dts | 1 + 9 files changed, 15 insertions(+) diff --git a/arch/arm/boot/dts/exynos4210-origen.dts b/arch/arm/boot/dts/exynos4210-origen.dts index f767c425d0b5d2..b811461414023f 100644 --- a/arch/arm/boot/dts/exynos4210-origen.dts +++ b/arch/arm/boot/dts/exynos4210-origen.dts @@ -31,6 +31,7 @@ chosen { bootargs ="root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC2,115200 init=/linuxrc"; + stdout-path = &serial_2; }; regulators { diff --git a/arch/arm/boot/dts/exynos4210-smdkv310.dts b/arch/arm/boot/dts/exynos4210-smdkv310.dts index 676e6e0c8cf396..86216fff1b4f42 100644 --- a/arch/arm/boot/dts/exynos4210-smdkv310.dts +++ b/arch/arm/boot/dts/exynos4210-smdkv310.dts @@ -27,6 +27,7 @@ chosen { bootargs = "root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC1,115200 init=/linuxrc"; + stdout-path = &serial_1; }; sdhci@12530000 { diff --git a/arch/arm/boot/dts/exynos4210-trats.dts b/arch/arm/boot/dts/exynos4210-trats.dts index e3879c0eaf7897..902d08c2ef4eaa 100644 --- a/arch/arm/boot/dts/exynos4210-trats.dts +++ b/arch/arm/boot/dts/exynos4210-trats.dts @@ -28,6 +28,7 @@ chosen { bootargs = "console=ttySAC2,115200N8 root=/dev/mmcblk0p5 rootwait earlyprintk panic=5"; + stdout-path = &serial_2; }; regulators { diff --git a/arch/arm/boot/dts/exynos4210-universal_c210.dts b/arch/arm/boot/dts/exynos4210-universal_c210.dts index 01f7d3cfdd021d..d4fbc0799db4de 100644 --- a/arch/arm/boot/dts/exynos4210-universal_c210.dts +++ b/arch/arm/boot/dts/exynos4210-universal_c210.dts @@ -26,6 +26,7 @@ chosen { bootargs = "console=ttySAC2,115200N8 root=/dev/mmcblk0p5 rw rootwait earlyprintk panic=5 maxcpus=1"; + stdout-path = &serial_2; }; sysram@02020000 { diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index 07e33c7e683b6a..c49efdfdcb061e 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -12,6 +12,10 @@ #include "exynos4412.dtsi" / { + chosen { + stdout-path = &serial_1; + }; + firmware@0204F000 { compatible = "samsung,secure-firmware"; reg = <0x0204F000 0x1000>; diff --git a/arch/arm/boot/dts/exynos4412-origen.dts b/arch/arm/boot/dts/exynos4412-origen.dts index de15114fd07caf..bd8b73077d41fa 100644 --- a/arch/arm/boot/dts/exynos4412-origen.dts +++ b/arch/arm/boot/dts/exynos4412-origen.dts @@ -26,6 +26,7 @@ chosen { bootargs ="console=ttySAC2,115200"; + stdout-path = &serial_2; }; firmware@0203F000 { diff --git a/arch/arm/boot/dts/exynos4412-smdk4412.dts b/arch/arm/boot/dts/exynos4412-smdk4412.dts index ded0b70f764485..b9256afbcc683e 100644 --- a/arch/arm/boot/dts/exynos4412-smdk4412.dts +++ b/arch/arm/boot/dts/exynos4412-smdk4412.dts @@ -25,6 +25,7 @@ chosen { bootargs ="root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC1,115200 init=/linuxrc"; + stdout-path = &serial_1; }; g2d@10800000 { diff --git a/arch/arm/boot/dts/exynos4412-tiny4412.dts b/arch/arm/boot/dts/exynos4412-tiny4412.dts index ea6929d9c6212f..d46fd4c2aeaa5a 100644 --- a/arch/arm/boot/dts/exynos4412-tiny4412.dts +++ b/arch/arm/boot/dts/exynos4412-tiny4412.dts @@ -18,6 +18,10 @@ model = "FriendlyARM TINY4412 board based on Exynos4412"; compatible = "friendlyarm,tiny4412", "samsung,exynos4412", "samsung,exynos4"; + chosen { + stdout-path = &serial_0; + }; + memory { reg = <0x40000000 0x40000000>; }; diff --git a/arch/arm/boot/dts/exynos4412-trats2.dts b/arch/arm/boot/dts/exynos4412-trats2.dts index 7d3c49b2eb1796..fc81d39d1dbdfd 100644 --- a/arch/arm/boot/dts/exynos4412-trats2.dts +++ b/arch/arm/boot/dts/exynos4412-trats2.dts @@ -32,6 +32,7 @@ chosen { bootargs = "console=ttySAC2,115200N8 root=/dev/mmcblk0p5 rootwait earlyprintk panic=5"; + stdout-path = &serial_2; }; firmware@0204F000 { From e881497d831ba52f07e56649db67136b35b7b31c Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Fri, 23 Jan 2015 18:43:41 +0100 Subject: [PATCH 677/788] patchset: [PATCH v4 0/2] serial: samsung: add support for early console --- PATCHES | 1 + 1 file changed, 1 insertion(+) diff --git a/PATCHES b/PATCHES index 6aa73cf3890b5c..a9c25e72c6c70d 100644 --- a/PATCHES +++ b/PATCHES @@ -4,3 +4,4 @@ [PATCHv3 0/8] devfreq: Add generic exynos memory-bus frequency driver [PATCH v6 00/18] thermal: exynos: Thermal code rework to use device tree [PATCH v5 00/18] Exynos SYSMMU (IOMMU) integration with DT and DMA-mapping subsystem +[PATCH v4 0/2] serial: samsung: add support for early console From 2b4309453dc5255e9c635bdcfa669103d21af2e0 Mon Sep 17 00:00:00 2001 From: Kamil Debski Date: Thu, 22 Jan 2015 17:04:33 +0100 Subject: [PATCH 678/788] ARM: dts: add hdmi cec driver to exynos4412-odroidu3 Add device tree node for the s5p-cec hdmi CEC driver. Signed-off-by: Kamil Debski --- arch/arm/boot/dts/exynos4412-odroid-common.dtsi | 7 +++++++ arch/arm/boot/dts/exynos4412-odroidu3.dts | 13 +++++++++++++ 2 files changed, 20 insertions(+) diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index c49efdfdcb061e..e038ffda6db47e 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -469,6 +469,13 @@ }; }; }; + + hdmi_cec: hdmi-cec { + samsung,pins = "gpx3-6"; + samsung,pin-function = <3>; + samsung,pin-pud = <0>; + samsung,pin-drv = <0>; + }; }; &ppmu_dmc0 { diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts b/arch/arm/boot/dts/exynos4412-odroidu3.dts index 03bdb46244350d..b7a1e47430d778 100644 --- a/arch/arm/boot/dts/exynos4412-odroidu3.dts +++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts @@ -37,6 +37,19 @@ pwms = <&pwm 0 10000 0>; status = "okay"; }; + + hdmicec: cec@100B0000 { + compatible = "samsung,s5p-cec"; + reg = <0x100B0000 0x200>; + interrupts = <0 114 0>; + clocks = <&clock CLK_HDMI_CEC>; + clock-names = "hdmicec"; + samsung,syscon-phandle = <&pmu_system_controller>; + cec-gpio = <&gpx3 6 0>; + pinctrl-names = "default"; + pinctrl-0 = <&hdmi_cec>; + status = "okay"; + }; }; &pwm { From eb0da03172a19a1faf4587f50c8ec862034d70d6 Mon Sep 17 00:00:00 2001 From: Kamil Debski Date: Thu, 22 Jan 2015 17:04:34 +0100 Subject: [PATCH 679/788] media: rc: Add cec protocol handling Add cec protocol handling the RC framework. Signed-off-by: Kamil Debski --- drivers/media/rc/keymaps/Makefile | 1 + drivers/media/rc/keymaps/rc-cec.c | 133 ++++++++++++++++++++++++++++++ drivers/media/rc/rc-main.c | 1 + include/media/rc-core.h | 1 + include/media/rc-map.h | 5 +- 5 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 drivers/media/rc/keymaps/rc-cec.c diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index abf60794223d36..56f10d6b655ee5 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-behold.o \ rc-behold-columbus.o \ rc-budget-ci-old.o \ + rc-cec.o \ rc-cinergy-1400.o \ rc-cinergy.o \ rc-delock-61959.o \ diff --git a/drivers/media/rc/keymaps/rc-cec.c b/drivers/media/rc/keymaps/rc-cec.c new file mode 100644 index 00000000000000..f2826c56012ada --- /dev/null +++ b/drivers/media/rc/keymaps/rc-cec.c @@ -0,0 +1,133 @@ +/* Keytable for the CEC remote control + * + * Copyright (c) 2015 by Kamil Debski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include + +/* CEC Spec "High-Definition Multimedia Interface Specification" can be obtained + * here: http://xtreamerdev.googlecode.com/files/CEC_Specs.pdf + * The list of control codes is listed in Table 27: User Control Codes p. 95 */ + +static struct rc_map_table cec[] = { + { 0x00, KEY_SELECT }, /* XXX CEC Spec: Select, should it be KEY_SELECT or KEY_OK? */ + { 0x01, KEY_UP }, + { 0x02, KEY_DOWN }, + { 0x03, KEY_LEFT }, + { 0x04, KEY_RIGHT }, + /* XXX 0x05-0x08 CEC Spec: Right-Up, Right-Down, Left-Up, Left-Down */ + { 0x09, KEY_CONTEXT_MENU }, /* CEC Spec: Root Menu - see Note 2 */ + /* Note 2: This is the initial display that a device shows. It is + * device-dependent and can be, for example, a contents menu, setup + * menu, favorite menu or other menu. The actual menu displayed + * may also depend on the device’s current state. */ + { 0x0a, KEY_SETUP }, + { 0x0b, KEY_MENU }, /* CEC Spec: Contents Menu */ + { 0x0c, KEY_FAVORITES }, /* CEC Spec: Favorite Menu */ + { 0x0d, KEY_EXIT }, + /* 0x0e-0x1f: Reserved */ + /* 0x20-0x29: Keys 0 to 9 */ + { 0x20, KEY_0 }, + { 0x21, KEY_1 }, + { 0x22, KEY_2 }, + { 0x23, KEY_3 }, + { 0x24, KEY_4 }, + { 0x25, KEY_5 }, + { 0x26, KEY_6 }, + { 0x27, KEY_7 }, + { 0x28, KEY_8 }, + { 0x29, KEY_9 }, + { 0x2a, KEY_DOT }, + { 0x2b, KEY_ENTER }, + { 0x2c, KEY_CLEAR }, + /* 0x2d-0x2e: Reserved */ + /* XXX 0x2f: CEC Spec: Next Favorite */ + { 0x30, KEY_CHANNELUP }, + { 0x31, KEY_CHANNELDOWN }, + { 0x32, KEY_PREVIOUS }, /* CEC Spec: Previous Channel */ + { 0x33, KEY_SOUND }, /* CEC Spec: Sound Select */ + /* XXX 0x34: CEC Spec: Input Select */ + { 0x35, KEY_INFO }, /* CEC Spec: Display Information */ + { 0x36, KEY_HELP }, + { 0x37, KEY_PAGEUP }, + { 0x38, KEY_PAGEDOWN }, + /* 0x39-0x3f: Reserved */ + { 0x40, KEY_POWER }, + { 0x41, KEY_VOLUMEUP }, + { 0x42, KEY_VOLUMEDOWN }, + { 0x43, KEY_MUTE }, + { 0x44, KEY_PLAY }, + { 0x45, KEY_STOP }, /* XXX CEC Spec: Stop, what about KEY_STOPCD? */ + { 0x46, KEY_PAUSE },/* XXX CEC Spec: Pause, what about KEY_PAUSECD? */ + { 0x47, KEY_RECORD }, + { 0x48, KEY_REWIND }, + { 0x49, KEY_FASTFORWARD }, + { 0x4a, KEY_EJECTCD }, /* CEC Spec: Eject */ + { 0x4b, KEY_FORWARD }, + { 0x4c, }, /* XXX */ + { 0x4d, KEY_STOP }, /* XXX CEC Spec: Stop-Record, what about KEY_STOPCD? */ + { 0x4e, KEY_PAUSE }, /* XXX CEC Spec: Pause-Record, what about KEY_PAUSECD? */ + /* 0x4f: Reserved */ + { 0x50, KEY_ANGLE }, + { 0x51, KEY_SUBTITLE }, /* XXX CEC Spec: Sub picture, should it be KEY_SUBTITLE or something else? */ + { 0x52, KEY_VIDEO }, /* XXX CEC Spec: Video on Demand / input.h: AL Movie Browser, maybe KEY_DIRECTORY? */ + { 0x53, KEY_EPG }, + { 0x54, KEY_TIME }, /* XXX CEC Spec: Timer */ + { 0x55, KEY_CONFIG }, + /* 0x56-0x5f: Reserved */ + { 0x60, KEY_PLAY }, /* XXX CEC Spec: Play Function */ + { 0x61, KEY_PLAYPAUSE }, /* XXX CEC Spec: Pause-Play Function */ + { 0x62, KEY_RECORD }, /* XXX CEC Spec: Record Function */ + { 0x63, KEY_PAUSE }, /* XXX CEC Spec: Pause-Record Function */ + { 0x64, KEY_STOP }, /* XXX CEC Spec: Stop Function */ + { 0x65, KEY_MUTE }, /* XXX CEC Spec: Mute Function */ + /* 0x66: CEC Spec: Restore Volume Function */ + { 0x67, KEY_TUNER }, /* XXX CEC Spec: Tune Function */ + { 0x68, KEY_MEDIA }, /* CEC Spec: Select Media Function */ + { 0x69, KEY_SWITCHVIDEOMODE} /* XXX CEC Spec: Select A/V Input Function */, + { 0x6a, KEY_AUDIO} /* CEC Spec: Select Audio Input Function */, + { 0x6b, KEY_POWER} /* CEC Spec: Power Toggle Function */, + { 0x6c, KEY_SLEEP} /* XXX CEC Spec: Power Off Function */, + { 0x6d, KEY_WAKEUP} /* XXX CEC Spec: Power On Function */, + /* 0x6e-0x70: Reserved */ + { 0x71, KEY_BLUE }, /* XXX CEC Spec: F1 (Blue) */ + { 0x72, KEY_RED }, /* XXX CEC Spec: F2 (Red) */ + { 0x73, KEY_GREEN }, /* XXX CEC Spec: F3 (Green) */ + { 0x74, KEY_YELLOW }, /* XXX CEC Spec: F4 (Yellow) */ + { 0x75, KEY_F5 }, + { 0x76, KEY_CONNECT }, /* XXX CEC Spec: Data - see Note 3 */ + /* Note 3: This is used, for example, to enter or leave a digital TV + * data broadcast application. */ + /* 0x77-0xff: Reserved */ +}; + +static struct rc_map_list cec_map = { + .map = { + .scan = cec, + .size = ARRAY_SIZE(cec), + .rc_type = RC_TYPE_CEC, + .name = RC_MAP_CEC, + } +}; + +static int __init init_rc_map_cec(void) +{ + return rc_map_register(&cec_map); +} + +static void __exit exit_rc_map_cec(void) +{ + rc_map_unregister(&cec_map); +} + +module_init(init_rc_map_cec); +module_exit(exit_rc_map_cec); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Kamil Debski"); diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index f8c5e47a30aa70..37d1ce0c9616c2 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -801,6 +801,7 @@ static struct { { RC_BIT_MCE_KBD, "mce_kbd" }, { RC_BIT_LIRC, "lirc" }, { RC_BIT_XMP, "xmp" }, + { RC_BIT_CEC, "cec" }, }; /** diff --git a/include/media/rc-core.h b/include/media/rc-core.h index 2c7fbca40b69f0..7c9d15d6b91a31 100644 --- a/include/media/rc-core.h +++ b/include/media/rc-core.h @@ -32,6 +32,7 @@ do { \ enum rc_driver_type { RC_DRIVER_SCANCODE = 0, /* Driver or hardware generates a scancode */ RC_DRIVER_IR_RAW, /* Needs a Infra-Red pulse/space decoder */ + RC_DRIVER_CEC, }; /** diff --git a/include/media/rc-map.h b/include/media/rc-map.h index e7a1514075ecfc..2058a89d97fc83 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -32,6 +32,7 @@ enum rc_type { RC_TYPE_RC6_MCE = 17, /* MCE (Philips RC6-6A-32 subtype) protocol */ RC_TYPE_SHARP = 18, /* Sharp protocol */ RC_TYPE_XMP = 19, /* XMP protocol */ + RC_TYPE_CEC = 20, /* CEC protocol */ }; #define RC_BIT_NONE 0 @@ -55,6 +56,7 @@ enum rc_type { #define RC_BIT_RC6_MCE (1 << RC_TYPE_RC6_MCE) #define RC_BIT_SHARP (1 << RC_TYPE_SHARP) #define RC_BIT_XMP (1 << RC_TYPE_XMP) +#define RC_BIT_CEC (1 << RC_TYPE_CEC) #define RC_BIT_ALL (RC_BIT_UNKNOWN | RC_BIT_OTHER | RC_BIT_LIRC | \ RC_BIT_RC5 | RC_BIT_RC5X | RC_BIT_RC5_SZ | \ @@ -63,7 +65,7 @@ enum rc_type { RC_BIT_NEC | RC_BIT_SANYO | RC_BIT_MCE_KBD | \ RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 | RC_BIT_RC6_6A_24 | \ RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE | RC_BIT_SHARP | \ - RC_BIT_XMP) + RC_BIT_XMP | RC_BIT_CEC) #define RC_SCANCODE_UNKNOWN(x) (x) @@ -125,6 +127,7 @@ void rc_map_init(void); #define RC_MAP_BEHOLD_COLUMBUS "rc-behold-columbus" #define RC_MAP_BEHOLD "rc-behold" #define RC_MAP_BUDGET_CI_OLD "rc-budget-ci-old" +#define RC_MAP_CEC "rc-cec" #define RC_MAP_CINERGY_1400 "rc-cinergy-1400" #define RC_MAP_CINERGY "rc-cinergy" #define RC_MAP_DELOCK_61959 "rc-delock-61959" From 9e6f87095aeddc1844a41dba14e9af83194882ef Mon Sep 17 00:00:00 2001 From: Kamil Debski Date: Thu, 22 Jan 2015 17:04:35 +0100 Subject: [PATCH 680/788] cec: add new framework for cec support. Add the CEC framework. Signed-off-by: Hans Verkuil [k.debski@samsung.com: Merged CEC Updates commit by Hans Verkuil] [k.debski@samsung.com: Merged Update author commit by Hans Verkuil] [k.debski@samsung.com: change kthread handling when setting logical address] [k.debski@samsung.com: code cleanup] [k.debski@samsung.com: add missing CEC commands to match spec] [k.debski@samsung.com: add RC framework support] [k.debski@samsung.com: move and edit documentation] --- Documentation/cec.txt | 318 +++++++++++ drivers/media/Kconfig | 5 + drivers/media/Makefile | 2 + drivers/media/cec.c | 1111 ++++++++++++++++++++++++++++++++++++++ include/media/cec.h | 136 +++++ include/uapi/linux/cec.h | 276 ++++++++++ 6 files changed, 1848 insertions(+) create mode 100644 Documentation/cec.txt create mode 100644 drivers/media/cec.c create mode 100644 include/media/cec.h create mode 100644 include/uapi/linux/cec.h diff --git a/Documentation/cec.txt b/Documentation/cec.txt new file mode 100644 index 00000000000000..8a04be3a0c0dd5 --- /dev/null +++ b/Documentation/cec.txt @@ -0,0 +1,318 @@ +CEC Kernel Support +================== + +The CEC framework provides a unified kernel interface for use with HDMI CEC +hardware. It is designed to handle a multiple variants of hardware. Adding to +the flexibility of the framework it enables to set which parts of the CEC +protocol processing is handled by the hardware, by the driver and by the +userspace application. + + +The CEC Protocol +---------------- + +The CEC protocol enables cosumer electronic devices to communicate with each +other through the HDMI connection. The protocol uses logical addresses in the +communication. The logical address is strictly connected with the functionality +provided by the device. The TV acting as the communication hub is always +assigned address 0. The physicall addressis determined by physical connection +between devices. + +The protocol enables control of compatible devices with a single remote. +Synchronous power on/standby, instant playback with changing the content source +on the TV. + +The Kernel Interface +==================== + +CEC Adaptor +----------- + +#define CEC_LOG_ADDR_INVALID 0xff + +/* The maximum number of logical addresses one device can be assigned to. + * The CEC 2.0 spec allows for only 2 logical addresses at the moment. The + * Analog Devices CEC hardware supports 3. So let's go wild and go for 4. */ +#define CEC_MAX_LOG_ADDRS 4 + +/* The "Primary Device Type" */ +#define CEC_PRIM_DEVTYPE_TV 0 +#define CEC_PRIM_DEVTYPE_RECORD 1 +#define CEC_PRIM_DEVTYPE_TUNER 3 +#define CEC_PRIM_DEVTYPE_PLAYBACK 4 +#define CEC_PRIM_DEVTYPE_AUDIOSYSTEM 5 +#define CEC_PRIM_DEVTYPE_SWITCH 6 +#define CEC_PRIM_DEVTYPE_VIDEOPROC 7 + +/* The "All Device Types" flags (CEC 2.0) */ +#define CEC_FL_ALL_DEVTYPE_TV (1 << 7) +#define CEC_FL_ALL_DEVTYPE_RECORD (1 << 6) +#define CEC_FL_ALL_DEVTYPE_TUNER (1 << 5) +#define CEC_FL_ALL_DEVTYPE_PLAYBACK (1 << 4) +#define CEC_FL_ALL_DEVTYPE_AUDIOSYSTEM (1 << 3) +#define CEC_FL_ALL_DEVTYPE_SWITCH (1 << 2) +/* And if you wondering what happened to VIDEOPROC devices: those should + * be mapped to a SWITCH. */ + +/* The logical address types that the CEC device wants to claim */ +#define CEC_LOG_ADDR_TYPE_TV 0 +#define CEC_LOG_ADDR_TYPE_RECORD 1 +#define CEC_LOG_ADDR_TYPE_TUNER 2 +#define CEC_LOG_ADDR_TYPE_PLAYBACK 3 +#define CEC_LOG_ADDR_TYPE_AUDIOSYSTEM 4 +#define CEC_LOG_ADDR_TYPE_SPECIFIC 5 +#define CEC_LOG_ADDR_TYPE_UNREGISTERED 6 +/* Switches should use UNREGISTERED. + * Video processors should use SPECIFIC. */ + +/* The CEC version */ +#define CEC_VERSION_1_4B 5 +#define CEC_VERSION_2_0 6 + +struct cec_adapter { + /* internal fields removed */ + + u16 phys_addr; + u32 capabilities; + u8 version; + u8 num_log_addrs; + u8 prim_device[CEC_MAX_LOG_ADDRS]; + u8 log_addr_type[CEC_MAX_LOG_ADDRS]; + u8 log_addr[CEC_MAX_LOG_ADDRS]; + + int (*adap_enable)(struct cec_adapter *adap, bool enable); + int (*adap_log_addr)(struct cec_adapter *adap, u8 logical_addr); + int (*adap_transmit)(struct cec_adapter *adap, struct cec_msg *msg); + void (*adap_transmit_timed_out)(struct cec_adapter *adap); + + int (*received_tv)(struct cec_adapter *adap, struct cec_msg *msg); + int (*received_record)(struct cec_adapter *adap, struct cec_msg *msg); + int (*received_tuner)(struct cec_adapter *adap, struct cec_msg *msg); + int (*received_playback)(struct cec_adapter *adap, struct cec_msg *msg); + int (*received_audiosystem)(struct cec_adapter *adap, struct cec_msg *msg); + int (*received_switch)(struct cec_adapter *adap, struct cec_msg *msg); + int (*received_videoproc)(struct cec_adapter *adap, struct cec_msg *msg); + int (*received)(struct cec_adapter *adap, struct cec_msg *msg); +}; + +int cec_create_adapter(struct cec_adapter *adap, u32 caps); +void cec_delete_adapter(struct cec_adapter *adap); +int cec_transmit_msg(struct cec_adapter *adap, struct cec_data *data, bool block); + +/* Called by the adapter */ +void cec_adap_transmit_done(struct cec_adapter *adap, u32 status); +void cec_adap_received_msg(struct cec_adapter *adap, struct cec_msg *msg); + + +The device type defines are defined by the CEC standard. + +The cec_adapter structure represents the adapter. It has a number of +operations that have to be implemented in the driver: adap_enable() enables +or disables the physical adapter, adap_log_addr() tells the driver which +logical address should be configured. This may be called multiple times +to configure multiple logical addresses. Calling adap_enable(false) or +adap_log_addr(CEC_LOG_ADDR_INVALID) will clear all configured logical +addresses. + +The adap_transmit op will setup the hardware to send out the given CEC message. +This will return without waiting for the transmission to finish. The +adap_transmit_timed_out() function is called when the current transmission timed +out and the hardware needs to be informed of this (the hardware should go back +from transmitter to receiver mode). + +The adapter driver will also call into the adapter: it should call +cec_transmit_done() when a cec transfer was finalized and cec_received_msg() +when a new message was received. + +When a message is received the corresponding received() op is called depending +on the logical address it is received on. If the message is not handled by +that the received op is called as fallback. The driver can hook into these ops +and do whatever it needs to do in order to respond to the message. + +The driver has to call cec_create_adapter to initialize the structure. If +the 'caps' argument is non-zero, then it will also create a /dev/cecX +device node to allow userspace to interact with the CEC device. Userspace +can request those capabilities with the CEC_G_CAPS ioctl. + +In order for a CEC adapter to be configured it needs a physical address. +This is normally assigned by the driver. It is either 0.0.0.0 for a TV (aka +video receiver) or it is derived from the EDID that the source received +from the sink. This is normally set by the driver before enabling the CEC +adapter, or it is set from userspace in the case of CEC USB dongles (although +embedded systems might also want to set this manually). + +After enabling the CEC adapter it has to be configured. The CEC adapter has +to be informed for which CEC device types a logical address has to be found. +The CEC framework will attempt to find such logical addresses. If none are +found, then it will fall back to logical address Unregistered (15). + +When a CEC message is received the CEC framework will take care of the CEC +core messages CEC_OP_GET_CEC_VERSION, CEC_OP_GIVE_PHYS_ADDR and CEC_OP_ABORT. +Then it will call the received() op (if set), and finally it will queue it +for handling by userspace if create_devnode was true, or send back +FEATURE_ABORT if create_devnode was false. + +Drivers can also use the cec_transmit_msg() call to transmit a message. This +can either be fire-and-forget (the CEC framework will queue up messages in a +transmit queue), or a blocking wait until there is either an error or a +reply to the message. + + +The Userspace API +================= + +CEC communication +----------------- + +This is the main message struct: + +struct cec_msg { + __u32 len; + __u8 msg[16]; + __u32 status; + /* If non-zero, then wait for a reply with this opcode. + If there was an error when sending the msg or FeatureAbort + was returned, then reply is set to 0. + If reply is non-zero upon return, then len/msg are set to + the received message. + If reply is zero upon return and status has the CEC_TX_STATUS_FEATURE_ABORT + bit set, then len/msg are set to the received feature abort message. + If reply is zero upon return and status has the CEC_TX_STATUS_REPLY_TIMEOUT + bit set, then no reply was seen at all. + This field is ignored with CEC_RECEIVE. + If reply is non-zero for CEC_TRANSMIT and the message is a broadcast, + then -EINVAL is returned. + if reply is non-zero, then timeout is set to 1000 (the required maximum + response time). + */ + __u8 reply; + /* timeout (in ms) is used to timeout CEC_RECEIVE. + Set to 0 if you want to wait forever. */ + __u32 timeout; + struct timespec ts; +}; + +16 bytes for the message, the length of the message, a status value +in case of errors. Optionally you can request the CEC framework to +wait after transmitting the message until the 'reply' message is +returned (or Feature Abort). This is done asynchronously, i.e. it +does not require that the reply comes right after the transmit, but +other messages in between are allowed. + +#define CEC_TRANSMIT _IOWR('a', 1, struct cec_msg) +#define CEC_RECEIVE _IOWR('a', 2, struct cec_msg) + +With CEC_TRANSMIT you can transmit a message, either blocking or +non-blocking. With CEC_RECEIVE you can dequeue a pending received +message from the internal queue or wait for a message to arrive +(if called in blocking mode). + + +/* Userspace has to configure the adapter state (enable/disable) */ +#define CEC_CAP_STATE (1 << 0) +/* Userspace has to configure the physical address */ +#define CEC_CAP_PHYS_ADDR (1 << 1) +/* Userspace has to configure the logical addresses */ +#define CEC_CAP_LOG_ADDRS (1 << 2) +/* Userspace can transmit messages */ +#define CEC_CAP_TRANSMIT (1 << 3) +/* Userspace can receive messages */ +#define CEC_CAP_RECEIVE (1 << 4) + +struct cec_caps { + __u32 available_log_addrs; + __u32 capabilities; +}; + +#define CEC_G_CAPS _IOR('a', 0, struct cec_caps) + +Obtain some of the CEC adapter capabilities: the number of logical addresses +that the adapter can configure and what can be controlled from userspace. + +/* + Enable/disable the adapter. The S_ADAP_STATE ioctl is not available + unless CEC_CAP_STATE is set. + */ +#define CEC_G_ADAP_STATE _IOR('a', 5, __u32) +#define CEC_S_ADAP_STATE _IOW('a', 6, __u32) + +/* + phys_addr is either 0 (if this is the CEC root device) + or a valid physical address obtained from the sink's EDID + as read by this CEC device (if this is a source device) + or a physical address obtained and modified from a sink + EDID and used for a sink CEC device. + If nothing is connected, then phys_addr is 0xffff. + See HDMI 1.4b, section 8.7 (Physical Address). + + The S_ADAP_PHYS_ADDR ioctl is not available unless CEC_CAP_PHYS_ADDR + is set. + */ +#define CEC_G_ADAP_PHYS_ADDR _IOR('a', 7, __u16) +#define CEC_S_ADAP_PHYS_ADDR _IOW('a', 8, __u16) + +struct cec_log_addrs { + __u8 cec_version; + __u8 num_log_addrs; + __u8 primary_device_type[CEC_MAX_LOG_ADDRS]; + __u8 log_addr_type[CEC_MAX_LOG_ADDRS]; + __u8 log_addr[CEC_MAX_LOG_ADDRS]; + + /* CEC 2.0 */ + __u8 all_device_types; + __u8 features[CEC_MAX_LOG_ADDRS][12]; +}; + +/* + Configure the CEC adapter. + + The cec_version determines which CEC version should be followed. + + It will try to claim num_log_addrs devices. The log_addr_type array has + the logical address type that needs to be claimed for that device, and + the log_addr array will receive the actual logical address that was + claimed for that device or 0xff if no address could be claimed. + + The primary_device_type contains the primary device for each logical + address. + + For CEC 2.0 devices the all_device_types parameter to use with the + Report Features command, and 'features' contains the remaining parameters + (RC Profile and Device Features) to use in Report Features. + + An error is returned if the adapter is disabled or if there + is no physical address assigned or if cec_version is unknown. + + If no logical address of one or more of the given types could be claimed, + then log_addr will be set to CEC_LOG_ADDR_INVALID. + + If no logical address could be claimed at all, then num_log_addrs will + be set to 1, log_addr_type[0] to UNREGISTERED and log_addr[0] to 0xf. + + The S_ADAP_LOG_ADDRS ioctl is not available unless CEC_CAP_LOG_ADDRS + is set. + */ +#define CEC_G_ADAP_LOG_ADDRS _IOR('a', 3, struct cec_log_addrs) +#define CEC_S_ADAP_LOG_ADDRS _IOWR('a', 4, struct cec_log_addrs) + +The event ioctl is used to get a single struct cec_event if it was +previously posted by the driver by the cec_post_event function. + +#define CEC_G_EVENT _IOWR('a', 9, struct cec_event) + +Remote control handling +----------------------- + +The CEC framework provides two ways of handling the key messages of remote +control. In the first case, the CEC framework will handle these messages and +provide the keypressed via the RC framework. In the second case the messages +related to the key down/up events are not parsed by the framework and are +passed to the userspace as raw messages. + +Switching between these modes is done with a special ioctl. + +#define CEC_G_KEY_PASSTHROUGH _IOR('a', 10, __u8) +#define CEC_S_KEY_PASSTHROUGH _IOW('a', 11, __u8) +#define CEC_KEY_PASSTHROUGH_DISABLE 0 +#define CEC_KEY_PASSTHROUGH_ENABLE 1 + diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 49cd30870e0d6f..e0653a1150c923 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -15,6 +15,11 @@ if MEDIA_SUPPORT comment "Multimedia core support" +config CEC + tristate "CEC API (EXPERIMENTAL)" + ---help--- + Enable the CEC API. + # # Multimedia support - automatically enable V4L2 and DVB core # diff --git a/drivers/media/Makefile b/drivers/media/Makefile index e608bbce0c3546..db660146f93e93 100644 --- a/drivers/media/Makefile +++ b/drivers/media/Makefile @@ -2,6 +2,8 @@ # Makefile for the kernel multimedia device drivers. # +obj-$(CONFIG_CEC) += cec.o + media-objs := media-device.o media-devnode.o media-entity.o # diff --git a/drivers/media/cec.c b/drivers/media/cec.c new file mode 100644 index 00000000000000..7a2d081624a348 --- /dev/null +++ b/drivers/media/cec.c @@ -0,0 +1,1111 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CEC_NUM_DEVICES 256 +#define CEC_NAME "cec" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "debug level (0-1)"); + +struct cec_transmit_notifier { + struct completion c; + struct cec_data *data; +}; + +#define dprintk(fmt, arg...) \ + do { \ + if (debug) \ + pr_info("cec-%s: " fmt, adap->name , ## arg); \ + } while(0) + +static dev_t cec_dev_t; + +/* Active devices */ +static DEFINE_MUTEX(cec_devnode_lock); +static DECLARE_BITMAP(cec_devnode_nums, CEC_NUM_DEVICES); + +/* dev to cec_devnode */ +#define to_cec_devnode(cd) container_of(cd, struct cec_devnode, dev) + +static inline struct cec_devnode *cec_devnode_data(struct file *filp) +{ + return filp->private_data; +} + +static int cec_log_addr2idx(const struct cec_adapter *adap, u8 log_addr) +{ + int i; + + for (i = 0; i < adap->num_log_addrs; i++) + if (adap->log_addr[i] == log_addr) + return i; + return -1; +} + +static unsigned cec_log_addr2dev(const struct cec_adapter *adap, u8 log_addr) +{ + int i = cec_log_addr2idx(adap, log_addr); + + return adap->prim_device[i < 0 ? 0 : i]; +} + +/* Called when the last user of the cec device exits. */ +static void cec_devnode_release(struct device *cd) +{ + struct cec_devnode *cecdev = to_cec_devnode(cd); + + mutex_lock(&cec_devnode_lock); + + /* Delete the cdev on this minor as well */ + cdev_del(&cecdev->cdev); + + /* Mark device node number as free */ + clear_bit(cecdev->minor, cec_devnode_nums); + + mutex_unlock(&cec_devnode_lock); + + /* Release cec_devnode and perform other cleanups as needed. */ + if (cecdev->release) + cecdev->release(cecdev); +} + +static struct bus_type cec_bus_type = { + .name = CEC_NAME, +}; + +static bool cec_sleep(struct cec_adapter *adap, int timeout) +{ + bool timed_out = false; + + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(&adap->kthread_waitq, &wait); + if (!kthread_should_stop()) { + if (timeout < 0) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + } else { + timed_out = !schedule_timeout_interruptible + (msecs_to_jiffies(timeout)); + } + } + + remove_wait_queue(&adap->kthread_waitq, &wait); + return timed_out; +} + +/* + * Main CEC state machine + * + * In the IDLE state the CEC adapter is ready to receive or transmit messages. + * If it is woken up it will check if a new message is queued, and if so it + * will be transmitted and the state will go to TRANSMITTING. + * + * When the transmit is marked as done the state machine will check if it + * should wait for a reply. If not, it will call the notifier and go back + * to the IDLE state. Else it will switch to the WAIT state and wait for a + * reply. When the reply arrives it will call the notifier and go back + * to IDLE state. + * + * For the transmit and the wait-for-reply states a timeout is used of + * 1 second as per the standard. + */ +static int cec_thread_func(void *data) +{ + struct cec_adapter *adap = data; + int timeout = -1; + + for (;;) { + bool timed_out = cec_sleep(adap, timeout); + + if (kthread_should_stop()) + break; + timeout = -1; + mutex_lock(&adap->lock); + dprintk("state %d timedout: %d tx: %d@%d\n", adap->state, + timed_out, adap->tx_qcount, adap->tx_qstart); + if (adap->state == CEC_ADAP_STATE_TRANSMITTING && timed_out) + adap->adap_transmit_timed_out(adap); + + if (adap->state == CEC_ADAP_STATE_WAIT || + adap->state == CEC_ADAP_STATE_TRANSMITTING) { + struct cec_data *data = adap->tx_queue + adap->tx_qstart; + + if (adap->state == CEC_ADAP_STATE_TRANSMITTING && + data->msg.reply && !timed_out && + data->msg.status == CEC_TX_STATUS_OK) { + adap->state = CEC_ADAP_STATE_WAIT; + timeout = 1000; + } else { + if (timed_out) { + data->msg.reply = 0; + if (adap->state == CEC_ADAP_STATE_TRANSMITTING) + data->msg.status = CEC_TX_STATUS_RETRY_TIMEOUT; + else + data->msg.status = CEC_TX_STATUS_REPLY_TIMEOUT; + } + adap->state = CEC_ADAP_STATE_IDLE; + if (data->func) { + mutex_unlock(&adap->lock); + data->func(adap, data, data->priv); + mutex_lock(&adap->lock); + } + adap->tx_qstart = (adap->tx_qstart + 1) % CEC_TX_QUEUE_SZ; + adap->tx_qcount--; + wake_up_interruptible(&adap->waitq); + } + } + if (adap->state == CEC_ADAP_STATE_IDLE && adap->tx_qcount) { + adap->state = CEC_ADAP_STATE_TRANSMITTING; + timeout = adap->tx_queue[adap->tx_qstart].msg.len == 1 ? 200 : 1000; + adap->adap_transmit(adap, &adap->tx_queue[adap->tx_qstart].msg); + mutex_unlock(&adap->lock); + continue; + } + mutex_unlock(&adap->lock); + } + return 0; +} + +static int cec_transmit_notify(struct cec_adapter *adap, struct cec_data *data, + void *priv) +{ + struct cec_transmit_notifier *n = priv; + + *(n->data) = *data; + complete(&n->c); + return 0; +} + +int cec_transmit_msg(struct cec_adapter *adap, struct cec_data *data, bool block) +{ + struct cec_transmit_notifier notifier; + struct cec_msg *msg = &data->msg; + int res = 0; + unsigned idx; + + if (msg->len == 0 || msg->len > 16) + return -EINVAL; + if (msg->reply && (msg->len == 1 || cec_msg_is_broadcast(msg))) + return -EINVAL; + if (msg->len > 1 && !cec_msg_is_broadcast(msg) && + cec_msg_initiator(msg) == cec_msg_destination(msg)) + return -EINVAL; + if (cec_msg_initiator(msg) != 0xf && + cec_log_addr2idx(adap, cec_msg_initiator(msg)) < 0) + return -EINVAL; + + if (msg->len == 1) + dprintk("cec_transmit_msg: 0x%02x%s\n", + msg->msg[0], !block ? " nb" : ""); + else if (msg->reply) + dprintk("cec_transmit_msg: 0x%02x 0x%02x (wait for 0x%02x)%s\n", + msg->msg[0], msg->msg[1], + msg->reply, !block ? " nb" : ""); + else + dprintk("cec_transmit_msg: 0x%02x 0x%02x%s\n", + msg->msg[0], msg->msg[1], + !block ? " nb" : ""); + + msg->status = 0; + memset(&msg->ts, 0, sizeof(msg->ts)); + if (msg->reply) + msg->timeout = 1000; + if (block) { + init_completion(¬ifier.c); + notifier.data = data; + data->func = cec_transmit_notify; + data->priv = ¬ifier; + } else { + data->func = NULL; + data->priv = NULL; + } + mutex_lock(&adap->lock); + idx = (adap->tx_qstart + adap->tx_qcount) % CEC_TX_QUEUE_SZ; + if (adap->tx_qcount == CEC_TX_QUEUE_SZ) { + res = -EBUSY; + } else { + adap->tx_queue[idx] = *data; + adap->tx_qcount++; + if (adap->state == CEC_ADAP_STATE_IDLE) + wake_up_interruptible(&adap->kthread_waitq); + } + mutex_unlock(&adap->lock); + if (res || !block) + return res; + wait_for_completion_interruptible(¬ifier.c); + return res; +} +EXPORT_SYMBOL_GPL(cec_transmit_msg); + +void cec_transmit_done(struct cec_adapter *adap, u32 status) +{ + struct cec_msg *msg; + + dprintk("cec_transmit_done\n"); + mutex_lock(&adap->lock); + if (adap->state == CEC_ADAP_STATE_TRANSMITTING) { + msg = &adap->tx_queue[adap->tx_qstart].msg; + msg->status = status; + if (status) + msg->reply = 0; + ktime_get_ts(&msg->ts); + wake_up_interruptible(&adap->kthread_waitq); + } + mutex_unlock(&adap->lock); +} +EXPORT_SYMBOL_GPL(cec_transmit_done); + +static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg) +{ + bool is_broadcast = cec_msg_is_broadcast(msg); + u8 dest_laddr = cec_msg_destination(msg); + u8 devtype = cec_log_addr2dev(adap, dest_laddr); + bool is_directed = cec_log_addr2idx(adap, dest_laddr) >= 0; + struct cec_data tx_data; + int res = 0; + unsigned idx; + + if (msg->len <= 1) + return 0; + if (!is_directed && !is_broadcast) + return 0; /* Not for us */ + + tx_data.msg.msg[0] = (msg->msg[0] << 4) | (msg->msg[0] >> 4); + tx_data.msg.reply = 0; + + if (adap->received) { + res = adap->received(adap, msg); + if (res != -ENOMSG) + return 0; + res = 0; + } + + switch (msg->msg[1]) { + case CEC_OP_GET_CEC_VERSION: + if (is_broadcast) + return 0; + tx_data.msg.len = 3; + tx_data.msg.msg[1] = CEC_OP_CEC_VERSION; + tx_data.msg.msg[2] = adap->version; + return cec_transmit_msg(adap, &tx_data, false); + + case CEC_OP_GIVE_PHYSICAL_ADDR: + if (!is_directed) + return 0; + /* Do nothing for CEC switches using addr 15 */ + if (devtype == CEC_PRIM_DEVTYPE_SWITCH && dest_laddr == 15) + return 0; + tx_data.msg.len = 5; + tx_data.msg.msg[1] = CEC_OP_REPORT_PHYSICAL_ADDR; + tx_data.msg.msg[2] = adap->phys_addr >> 8; + tx_data.msg.msg[3] = adap->phys_addr & 0xff; + tx_data.msg.msg[4] = devtype; + return cec_transmit_msg(adap, &tx_data, false); + + case CEC_OP_ABORT: + /* Do nothing for CEC switches */ + if (devtype == CEC_PRIM_DEVTYPE_SWITCH) + return 0; + tx_data.msg.len = 4; + tx_data.msg.msg[1] = CEC_OP_FEATURE_ABORT; + tx_data.msg.msg[2] = msg->msg[1]; + tx_data.msg.msg[3] = 4; /* Refused */ + return cec_transmit_msg(adap, &tx_data, false); + + case CEC_OP_USER_CONTROL_PRESSED: + if (adap->key_passthrough != CEC_KEY_PASSTHROUGH_ENABLE) { + rc_keydown(adap->rc, RC_TYPE_CEC, msg->msg[2], 0); + return 0; + } + break; + + case CEC_OP_USER_CONTROL_RELEASED: + if (adap->key_passthrough != CEC_KEY_PASSTHROUGH_ENABLE) { + rc_keyup(adap->rc); + return 0; + } + break; + } + + if ((adap->capabilities & CEC_CAP_RECEIVE) == 0) + return 0; + mutex_lock(&adap->lock); + idx = (adap->rx_qstart + adap->rx_qcount) % CEC_RX_QUEUE_SZ; + if (adap->rx_qcount == CEC_RX_QUEUE_SZ) { + res = -EBUSY; + } else { + adap->rx_queue[idx] = *msg; + adap->rx_qcount++; + wake_up_interruptible(&adap->waitq); + } + mutex_unlock(&adap->lock); + return res; +} + +int cec_receive_msg(struct cec_adapter *adap, struct cec_msg *msg, bool block) +{ + int res; + + do { + mutex_lock(&adap->lock); + if (adap->rx_qcount) { + *msg = adap->rx_queue[adap->rx_qstart]; + adap->rx_qstart = (adap->rx_qstart + 1) % CEC_RX_QUEUE_SZ; + adap->rx_qcount--; + res = 0; + } else { + res = -EAGAIN; + } + mutex_unlock(&adap->lock); + if (!block || !res) + break; + if (msg->timeout) { + res = wait_event_interruptible_timeout(adap->waitq, + adap->rx_qcount, msecs_to_jiffies(msg->timeout)); + if (res == 0) + res = -ETIMEDOUT; + else if (res > 0) + res = 0; + } else { + res = wait_event_interruptible(adap->waitq, + adap->rx_qcount); + } + } while (!res); + return res; +} +EXPORT_SYMBOL_GPL(cec_receive_msg); + +void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg) +{ + bool is_reply = false; + + mutex_lock(&adap->lock); + ktime_get_ts(&msg->ts); + dprintk("cec_received_msg: %02x %02x\n", msg->msg[0], msg->msg[1]); + if (!cec_msg_is_broadcast(msg) && msg->len > 1 && + adap->state == CEC_ADAP_STATE_WAIT) { + struct cec_msg *dst = &adap->tx_queue[adap->tx_qstart].msg; + + if (msg->msg[1] == dst->reply || + msg->msg[1] == CEC_OP_FEATURE_ABORT) { + *dst = *msg; + is_reply = true; + if (msg->msg[1] == CEC_OP_FEATURE_ABORT) { + dst->reply = 0; + dst->status = CEC_TX_STATUS_FEATURE_ABORT; + } + wake_up_interruptible(&adap->kthread_waitq); + } + } + mutex_unlock(&adap->lock); + if (!is_reply) + adap->recv_notifier(adap, msg); +} +EXPORT_SYMBOL_GPL(cec_received_msg); + +void cec_post_event(struct cec_adapter *adap, u32 event) +{ + unsigned idx; + + mutex_lock(&adap->lock); + if (adap->ev_qcount == CEC_EV_QUEUE_SZ) { + /* Drop oldest event */ + adap->ev_qstart = (adap->ev_qstart + 1) % CEC_EV_QUEUE_SZ; + adap->ev_qcount--; + } + + idx = (adap->ev_qstart + adap->ev_qcount) % CEC_EV_QUEUE_SZ; + + adap->ev_queue[idx].event = event; + ktime_get_ts(&adap->ev_queue[idx].ts); + adap->ev_qcount++; + mutex_unlock(&adap->lock); +} +EXPORT_SYMBOL_GPL(cec_post_event); + +static int cec_report_phys_addr(struct cec_adapter *adap, unsigned logical_addr) +{ + struct cec_data data; + + /* Report Physical Address */ + data.msg.len = 5; + data.msg.msg[0] = (logical_addr << 4) | 0x0f; + data.msg.msg[1] = CEC_OP_REPORT_PHYSICAL_ADDR; + data.msg.msg[2] = adap->phys_addr >> 8; + data.msg.msg[3] = adap->phys_addr & 0xff; + data.msg.msg[4] = cec_log_addr2dev(adap, logical_addr); + data.msg.reply = 0; + dprintk("config: la %d pa %x.%x.%x.%x\n", + logical_addr, cec_phys_addr_exp(adap->phys_addr)); + return cec_transmit_msg(adap, &data, true); +} + +int cec_enable(struct cec_adapter *adap, bool enable) +{ + int ret; + + mutex_lock(&adap->lock); + ret = adap->adap_enable(adap, enable); + if (ret) { + mutex_unlock(&adap->lock); + return ret; + } + if (!enable) { + adap->state = CEC_ADAP_STATE_DISABLED; + adap->tx_qcount = 0; + adap->rx_qcount = 0; + adap->ev_qcount = 0; + adap->num_log_addrs = 0; + } else { + adap->state = CEC_ADAP_STATE_UNCONF; + } + mutex_unlock(&adap->lock); + return 0; +} +EXPORT_SYMBOL_GPL(cec_enable); + +struct cec_log_addrs_int { + struct cec_adapter *adap; + struct cec_log_addrs log_addrs; + struct completion c; + bool free_on_exit; + int err; +}; + +static int cec_config_log_addrs(struct cec_adapter *adap, struct cec_log_addrs *log_addrs) +{ + static const u8 tv_log_addrs[] = { + 0, CEC_LOG_ADDR_INVALID + }; + static const u8 record_log_addrs[] = { + 1, 2, 9, 12, 13, CEC_LOG_ADDR_INVALID + }; + static const u8 tuner_log_addrs[] = { + 3, 6, 7, 10, 12, 13, CEC_LOG_ADDR_INVALID + }; + static const u8 playback_log_addrs[] = { + 4, 8, 11, 12, 13, CEC_LOG_ADDR_INVALID + }; + static const u8 audiosystem_log_addrs[] = { + 5, 12, 13, CEC_LOG_ADDR_INVALID + }; + static const u8 specific_use_log_addrs[] = { + 14, 12, 13, CEC_LOG_ADDR_INVALID + }; + static const u8 unregistered_log_addrs[] = { + CEC_LOG_ADDR_INVALID + }; + static const u8 *type2addrs[7] = { + [CEC_LOG_ADDR_TYPE_TV] = tv_log_addrs, + [CEC_LOG_ADDR_TYPE_RECORD] = record_log_addrs, + [CEC_LOG_ADDR_TYPE_TUNER] = tuner_log_addrs, + [CEC_LOG_ADDR_TYPE_PLAYBACK] = playback_log_addrs, + [CEC_LOG_ADDR_TYPE_AUDIOSYSTEM] = audiosystem_log_addrs, + [CEC_LOG_ADDR_TYPE_SPECIFIC] = specific_use_log_addrs, + [CEC_LOG_ADDR_TYPE_UNREGISTERED] = unregistered_log_addrs, + }; + struct cec_data data; + u32 claimed_addrs = 0; + int i, j; + int err; + + if (adap->phys_addr) { + /* The TV functionality can only map to physical address 0. + For any other address, try the Specific functionality + instead as per the spec. */ + for (i = 0; i < log_addrs->num_log_addrs; i++) + if (log_addrs->log_addr_type[i] == CEC_LOG_ADDR_TYPE_TV) + log_addrs->log_addr_type[i] = CEC_LOG_ADDR_TYPE_SPECIFIC; + } + + memcpy(adap->prim_device, log_addrs->primary_device_type, log_addrs->num_log_addrs); + dprintk("physical address: %x.%x.%x.%x, claim %d logical addresses\n", + cec_phys_addr_exp(adap->phys_addr), log_addrs->num_log_addrs); + adap->num_log_addrs = 0; + adap->state = CEC_ADAP_STATE_IDLE; + + /* TODO: remember last used logical addr type to achieve + faster logical address polling by trying that one first. + */ + for (i = 0; i < log_addrs->num_log_addrs; i++) { + const u8 *la_list = type2addrs[log_addrs->log_addr_type[i]]; + + if (kthread_should_stop()) + return -EINTR; + + for (j = 0; la_list[j] != CEC_LOG_ADDR_INVALID; j++) { + u8 log_addr = la_list[j]; + + if (claimed_addrs & (1 << log_addr)) + continue; + + /* Send polling message */ + data.msg.len = 1; + data.msg.msg[0] = 0xf0 | log_addr; + data.msg.reply = 0; + err = cec_transmit_msg(adap, &data, true); + if (err) + return err; + if (data.msg.status == CEC_TX_STATUS_RETRY_TIMEOUT) { + /* Message not acknowledged, so this logical + address is free to use. */ + claimed_addrs |= 1 << log_addr; + adap->log_addr[adap->num_log_addrs++] = log_addr; + log_addrs->log_addr[i] = log_addr; + err = adap->adap_log_addr(adap, log_addr); + dprintk("claim addr %d (%d)\n", log_addr, adap->prim_device[i]); + if (err) + return err; + cec_report_phys_addr(adap, log_addr); + if (adap->claimed_log_addr) + adap->claimed_log_addr(adap, i); + break; + } + } + } + if (adap->num_log_addrs == 0) { + if (log_addrs->num_log_addrs > 1) + dprintk("could not claim last %d addresses\n", log_addrs->num_log_addrs - 1); + adap->log_addr[adap->num_log_addrs++] = 15; + log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_UNREGISTERED; + log_addrs->log_addr[0] = 15; + log_addrs->num_log_addrs = 1; + err = adap->adap_log_addr(adap, 15); + dprintk("claim addr %d (%d)\n", 15, adap->prim_device[0]); + if (err) + return err; + cec_report_phys_addr(adap, 15); + if (adap->claimed_log_addr) + adap->claimed_log_addr(adap, 0); + } + return 0; +} + +static int cec_config_thread_func(void *arg) +{ + struct cec_log_addrs_int *cla_int = arg; + int err; + + cla_int->err = err = cec_config_log_addrs(cla_int->adap, &cla_int->log_addrs); + cla_int->adap->kthread_config = NULL; + if (cla_int->free_on_exit) + kfree(cla_int); + else + complete(&cla_int->c); + return err; +} + +int cec_claim_log_addrs(struct cec_adapter *adap, struct cec_log_addrs *log_addrs, bool block) +{ + struct cec_log_addrs_int *cla_int; + int i; + + if (adap->state == CEC_ADAP_STATE_DISABLED) + return -EINVAL; + + if (log_addrs->num_log_addrs == 0 || + log_addrs->num_log_addrs > CEC_MAX_LOG_ADDRS) + return -EINVAL; + if (log_addrs->cec_version != CEC_VERSION_1_4B && + log_addrs->cec_version != CEC_VERSION_2_0) + return -EINVAL; + if (log_addrs->num_log_addrs > 1) + for (i = 0; i < log_addrs->num_log_addrs; i++) + if (log_addrs->log_addr_type[i] == + CEC_LOG_ADDR_TYPE_UNREGISTERED) + return -EINVAL; + for (i = 0; i < log_addrs->num_log_addrs; i++) { + if (log_addrs->primary_device_type[i] > CEC_PRIM_DEVTYPE_VIDEOPROC) + return -EINVAL; + if (log_addrs->primary_device_type[i] == 2) + return -EINVAL; + if (log_addrs->log_addr_type[i] > CEC_LOG_ADDR_TYPE_UNREGISTERED) + return -EINVAL; + } + + /* For phys addr 0xffff only the Unregistered functionality is + allowed. */ + if (adap->phys_addr == 0xffff && + (log_addrs->num_log_addrs > 1 || + log_addrs->log_addr_type[0] != CEC_LOG_ADDR_TYPE_UNREGISTERED)) + return -EINVAL; + + cla_int = kzalloc(sizeof(*cla_int), GFP_KERNEL); + if (cla_int == NULL) + return -ENOMEM; + init_completion(&cla_int->c); + cla_int->free_on_exit = !block; + cla_int->adap = adap; + cla_int->log_addrs = *log_addrs; + adap->kthread_config = kthread_run(cec_config_thread_func, cla_int, "cec_log_addrs"); + if (block) { + wait_for_completion(&cla_int->c); + kfree(cla_int); + } + return 0; +} +EXPORT_SYMBOL_GPL(cec_claim_log_addrs); + +static ssize_t cec_read(struct file *filp, char __user *buf, + size_t sz, loff_t *off) +{ + struct cec_devnode *cecdev = cec_devnode_data(filp); + + if (!cec_devnode_is_registered(cecdev)) + return -EIO; + return 0; +} + +static ssize_t cec_write(struct file *filp, const char __user *buf, + size_t sz, loff_t *off) +{ + struct cec_devnode *cecdev = cec_devnode_data(filp); + + if (!cec_devnode_is_registered(cecdev)) + return -EIO; + return 0; +} + +static unsigned int cec_poll(struct file *filp, + struct poll_table_struct *poll) +{ + struct cec_devnode *cecdev = cec_devnode_data(filp); + struct cec_adapter *adap = to_cec_adapter(cecdev); + unsigned res = 0; + + if (!cec_devnode_is_registered(cecdev)) + return POLLERR | POLLHUP; + mutex_lock(&adap->lock); + if (adap->tx_qcount < CEC_TX_QUEUE_SZ) + res |= POLLOUT | POLLWRNORM; + if (adap->rx_qcount) + res |= POLLIN | POLLRDNORM; + poll_wait(filp, &adap->waitq, poll); + mutex_unlock(&adap->lock); + return res; +} + +static long cec_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct cec_devnode *cecdev = cec_devnode_data(filp); + struct cec_adapter *adap = to_cec_adapter(cecdev); + void __user *parg = (void __user *)arg; + int err; + + if (!cec_devnode_is_registered(cecdev)) + return -EIO; + + switch (cmd) { + case CEC_G_CAPS: { + struct cec_caps caps; + + caps.available_log_addrs = 3; + caps.capabilities = adap->capabilities; + if (copy_to_user(parg, &caps, sizeof(caps))) + return -EFAULT; + break; + } + + case CEC_TRANSMIT: { + struct cec_data data; + + if (!(adap->capabilities & CEC_CAP_TRANSMIT)) + return -ENOTTY; + if (copy_from_user(&data.msg, parg, sizeof(data.msg))) + return -EFAULT; + err = cec_transmit_msg(adap, &data, !(filp->f_flags & O_NONBLOCK)); + if (err) + return err; + if (copy_to_user(parg, &data.msg, sizeof(data.msg))) + return -EFAULT; + break; + } + + case CEC_RECEIVE: { + struct cec_data data; + + if (!(adap->capabilities & CEC_CAP_RECEIVE)) + return -ENOTTY; + if (copy_from_user(&data.msg, parg, sizeof(data.msg))) + return -EFAULT; + err = cec_receive_msg(adap, &data.msg, !(filp->f_flags & O_NONBLOCK)); + if (err) + return err; + if (copy_to_user(parg, &data.msg, sizeof(data.msg))) + return -EFAULT; + break; + } + + case CEC_G_EVENT: { + struct cec_event ev; + + mutex_lock(&adap->lock); + err = -EAGAIN; + if (adap->ev_qcount) { + err = 0; + ev = adap->ev_queue[adap->ev_qstart]; + adap->ev_qstart = (adap->ev_qstart + 1) % CEC_EV_QUEUE_SZ; + adap->ev_qcount--; + } + mutex_unlock(&adap->lock); + if (err) + return err; + if (copy_to_user((void __user *)arg, &ev, sizeof(ev))) + return -EFAULT; + break; + } + + case CEC_G_ADAP_STATE: { + u32 state = adap->state != CEC_ADAP_STATE_DISABLED; + + if (copy_to_user(parg, &state, sizeof(state))) + return -EFAULT; + break; + } + + case CEC_S_ADAP_STATE: { + u32 state; + + if (!(adap->capabilities & CEC_CAP_STATE)) + return -ENOTTY; + if (copy_from_user(&state, parg, sizeof(state))) + return -EFAULT; + if (!state && adap->state == CEC_ADAP_STATE_DISABLED) + return 0; + if (state && adap->state != CEC_ADAP_STATE_DISABLED) + return 0; + cec_enable(adap, !!state); + break; + } + + case CEC_G_ADAP_PHYS_ADDR: + if (copy_to_user(parg, &adap->phys_addr, sizeof(adap->phys_addr))) + return -EFAULT; + break; + + case CEC_S_ADAP_PHYS_ADDR: { + u16 phys_addr; + + if (!(adap->capabilities & CEC_CAP_PHYS_ADDR)) + return -ENOTTY; + if (copy_from_user(&phys_addr, parg, sizeof(phys_addr))) + return -EFAULT; + adap->phys_addr = phys_addr; + break; + } + + case CEC_G_ADAP_LOG_ADDRS: { + struct cec_log_addrs log_addrs; + + log_addrs.cec_version = adap->version; + log_addrs.num_log_addrs = adap->num_log_addrs; + memcpy(log_addrs.primary_device_type, adap->prim_device, CEC_MAX_LOG_ADDRS); + memcpy(log_addrs.log_addr_type, adap->log_addr_type, CEC_MAX_LOG_ADDRS); + memcpy(log_addrs.log_addr, adap->log_addr, CEC_MAX_LOG_ADDRS); + + if (copy_to_user(parg, &log_addrs, sizeof(log_addrs))) + return -EFAULT; + break; + } + + case CEC_S_ADAP_LOG_ADDRS: { + struct cec_log_addrs log_addrs; + + if (!(adap->capabilities & CEC_CAP_LOG_ADDRS)) + return -ENOTTY; + if (copy_from_user(&log_addrs, parg, sizeof(log_addrs))) + return -EFAULT; + err = cec_claim_log_addrs(adap, &log_addrs, true); + if (err) + return err; + + if (copy_to_user(parg, &log_addrs, sizeof(log_addrs))) + return -EFAULT; + break; + } + + case CEC_G_KEY_PASSTHROUGH: { + if (put_user(adap->key_passthrough, (__u8 __user *)parg)) + return -EFAULT; + break; + } + + case CEC_S_KEY_PASSTHROUGH: { + __u8 state; + if (get_user(state, (__u8 __user *)parg)) + return -EFAULT; + if (state != CEC_KEY_PASSTHROUGH_DISABLE && + state != CEC_KEY_PASSTHROUGH_ENABLE) + return -EINVAL; + adap->key_passthrough = state; + break; + } + + default: + return -ENOTTY; + } + return 0; +} + +/* Override for the open function */ +static int cec_open(struct inode *inode, struct file *filp) +{ + struct cec_devnode *cecdev; + + /* Check if the cec device is available. This needs to be done with + * the cec_devnode_lock held to prevent an open/unregister race: + * without the lock, the device could be unregistered and freed between + * the cec_devnode_is_registered() and get_device() calls, leading to + * a crash. + */ + mutex_lock(&cec_devnode_lock); + cecdev = container_of(inode->i_cdev, struct cec_devnode, cdev); + /* return ENXIO if the cec device has been removed + already or if it is not registered anymore. */ + if (!cec_devnode_is_registered(cecdev)) { + mutex_unlock(&cec_devnode_lock); + return -ENXIO; + } + /* and increase the device refcount */ + get_device(&cecdev->dev); + mutex_unlock(&cec_devnode_lock); + + filp->private_data = cecdev; + + return 0; +} + +/* Override for the release function */ +static int cec_release(struct inode *inode, struct file *filp) +{ + struct cec_devnode *cecdev = cec_devnode_data(filp); + int ret = 0; + + /* decrease the refcount unconditionally since the release() + return value is ignored. */ + put_device(&cecdev->dev); + filp->private_data = NULL; + return ret; +} + +static const struct file_operations cec_devnode_fops = { + .owner = THIS_MODULE, + .read = cec_read, + .write = cec_write, + .open = cec_open, + .unlocked_ioctl = cec_ioctl, + .release = cec_release, + .poll = cec_poll, + .llseek = no_llseek, +}; + +/** + * cec_devnode_register - register a cec device node + * @cecdev: cec device node structure we want to register + * + * The registration code assigns minor numbers and registers the new device node + * with the kernel. An error is returned if no free minor number can be found, + * or if the registration of the device node fails. + * + * Zero is returned on success. + * + * Note that if the cec_devnode_register call fails, the release() callback of + * the cec_devnode structure is *not* called, so the caller is responsible for + * freeing any data. + */ +static int __must_check cec_devnode_register(struct cec_devnode *cecdev, + struct module *owner) +{ + int minor; + int ret; + + /* Part 1: Find a free minor number */ + mutex_lock(&cec_devnode_lock); + minor = find_next_zero_bit(cec_devnode_nums, CEC_NUM_DEVICES, 0); + if (minor == CEC_NUM_DEVICES) { + mutex_unlock(&cec_devnode_lock); + pr_err("could not get a free minor\n"); + return -ENFILE; + } + + set_bit(minor, cec_devnode_nums); + mutex_unlock(&cec_devnode_lock); + + cecdev->minor = minor; + + /* Part 2: Initialize and register the character device */ + cdev_init(&cecdev->cdev, &cec_devnode_fops); + cecdev->cdev.owner = owner; + + ret = cdev_add(&cecdev->cdev, MKDEV(MAJOR(cec_dev_t), cecdev->minor), 1); + if (ret < 0) { + pr_err("%s: cdev_add failed\n", __func__); + goto error; + } + + /* Part 3: Register the cec device */ + cecdev->dev.bus = &cec_bus_type; + cecdev->dev.devt = MKDEV(MAJOR(cec_dev_t), cecdev->minor); + cecdev->dev.release = cec_devnode_release; + if (cecdev->parent) + cecdev->dev.parent = cecdev->parent; + dev_set_name(&cecdev->dev, "cec%d", cecdev->minor); + ret = device_register(&cecdev->dev); + if (ret < 0) { + pr_err("%s: device_register failed\n", __func__); + goto error; + } + + /* Part 4: Activate this minor. The char device can now be used. */ + set_bit(CEC_FLAG_REGISTERED, &cecdev->flags); + + return 0; + +error: + cdev_del(&cecdev->cdev); + clear_bit(cecdev->minor, cec_devnode_nums); + return ret; +} + +/** + * cec_devnode_unregister - unregister a cec device node + * @cecdev: the device node to unregister + * + * This unregisters the passed device. Future open calls will be met with + * errors. + * + * This function can safely be called if the device node has never been + * registered or has already been unregistered. + */ +static void cec_devnode_unregister(struct cec_devnode *cecdev) +{ + /* Check if cecdev was ever registered at all */ + if (!cec_devnode_is_registered(cecdev)) + return; + + mutex_lock(&cec_devnode_lock); + clear_bit(CEC_FLAG_REGISTERED, &cecdev->flags); + mutex_unlock(&cec_devnode_lock); + device_unregister(&cecdev->dev); +} + +int cec_create_adapter(struct cec_adapter *adap, const char *name, u32 caps) +{ + int res = 0; + + adap->state = CEC_ADAP_STATE_DISABLED; + adap->name = name; + adap->phys_addr = 0xffff; + adap->capabilities = caps; + adap->version = CEC_VERSION_1_4B; + mutex_init(&adap->lock); + adap->kthread = kthread_run(cec_thread_func, adap, name); + init_waitqueue_head(&adap->kthread_waitq); + init_waitqueue_head(&adap->waitq); + if (IS_ERR(adap->kthread)) { + pr_err("cec-%s: kernel_thread() failed\n", name); + return PTR_ERR(adap->kthread); + } + if (caps) { + res = cec_devnode_register(&adap->devnode, adap->owner); + if (res) + kthread_stop(adap->kthread); + } + adap->recv_notifier = cec_receive_notify; + + /* Prepare the RC input device */ + adap->rc = rc_allocate_device(); + if (!adap->rc) { + pr_err("cec-%s: failed to allocate memory for rc_dev\n", name); + cec_devnode_unregister(&adap->devnode); + kthread_stop(adap->kthread); + return -ENOMEM; + } + + snprintf(adap->input_name, sizeof(adap->input_name), "RC for %s", name); + snprintf(adap->input_phys, sizeof(adap->input_phys), "%s/input0", name); + strncpy(adap->input_drv, name, sizeof(adap->input_drv)); + + adap->rc->input_name = adap->input_name; + adap->rc->input_phys = adap->input_phys; + adap->rc->dev.parent = &adap->devnode.dev; + adap->rc->driver_name = adap->input_drv; + adap->rc->driver_type = RC_DRIVER_CEC; + adap->rc->priv = adap; + adap->rc->map_name = RC_MAP_CEC; + adap->rc->timeout = MS_TO_NS(100); + adap->rc->allowed_protocols = RC_BIT_CEC; + res = rc_register_device(adap->rc); + + if (res) { + pr_err("cec-%s: failed to prepare input device\n", name); + cec_devnode_unregister(&adap->devnode); + rc_free_device(adap->rc); + kthread_stop(adap->kthread); + } + + return res; +} +EXPORT_SYMBOL_GPL(cec_create_adapter); + +void cec_delete_adapter(struct cec_adapter *adap) +{ + if (adap->kthread == NULL) + return; + kthread_stop(adap->kthread); + if (adap->kthread_config) + kthread_stop(adap->kthread_config); + adap->state = CEC_ADAP_STATE_DISABLED; + if (cec_devnode_is_registered(&adap->devnode)) + cec_devnode_unregister(&adap->devnode); +} +EXPORT_SYMBOL_GPL(cec_delete_adapter); + +/* + * Initialise cec for linux + */ +static int __init cec_devnode_init(void) +{ + int ret; + + pr_info("Linux cec interface: v0.10\n"); + ret = alloc_chrdev_region(&cec_dev_t, 0, CEC_NUM_DEVICES, + CEC_NAME); + if (ret < 0) { + pr_warn("cec: unable to allocate major\n"); + return ret; + } + + ret = bus_register(&cec_bus_type); + if (ret < 0) { + unregister_chrdev_region(cec_dev_t, CEC_NUM_DEVICES); + pr_warn("cec: bus_register failed\n"); + return -EIO; + } + + return 0; +} + +static void __exit cec_devnode_exit(void) +{ + bus_unregister(&cec_bus_type); + unregister_chrdev_region(cec_dev_t, CEC_NUM_DEVICES); +} + +subsys_initcall(cec_devnode_init); +module_exit(cec_devnode_exit) + +MODULE_AUTHOR("Hans Verkuil "); +MODULE_DESCRIPTION("Device node registration for cec drivers"); +MODULE_LICENSE("GPL"); diff --git a/include/media/cec.h b/include/media/cec.h new file mode 100644 index 00000000000000..ff21e22497e406 --- /dev/null +++ b/include/media/cec.h @@ -0,0 +1,136 @@ +#ifndef _CEC_DEVNODE_H +#define _CEC_DEVNODE_H + +#include +#include +#include +#include +#include +#include +#include + +#define cec_phys_addr_exp(pa) \ + ((pa) >> 12), ((pa) >> 8) & 0xf, ((pa) >> 4) & 0xf, (pa) & 0xf + +/* + * Flag to mark the cec_devnode struct as registered. Drivers must not touch + * this flag directly, it will be set and cleared by cec_devnode_register and + * cec_devnode_unregister. + */ +#define CEC_FLAG_REGISTERED 0 + +/** + * struct cec_devnode - cec device node + * @parent: parent device + * @minor: device node minor number + * @flags: flags, combination of the CEC_FLAG_* constants + * + * This structure represents a cec-related device node. + * + * The @parent is a physical device. It must be set by core or device drivers + * before registering the node. + */ +struct cec_devnode { + /* sysfs */ + struct device dev; /* cec device */ + struct cdev cdev; /* character device */ + struct device *parent; /* device parent */ + + /* device info */ + int minor; + unsigned long flags; /* Use bitops to access flags */ + + /* callbacks */ + void (*release)(struct cec_devnode *cecdev); +}; + +static inline int cec_devnode_is_registered(struct cec_devnode *cecdev) +{ + return test_bit(CEC_FLAG_REGISTERED, &cecdev->flags); +} + +struct cec_adapter; +struct cec_data; + +typedef int (*cec_notify)(struct cec_adapter *adap, struct cec_data *data, void *priv); +typedef int (*cec_recv_notify)(struct cec_adapter *adap, struct cec_msg *msg); + +struct cec_data { + struct cec_msg msg; + cec_notify func; + void *priv; +}; + +/* Unconfigured state */ +#define CEC_ADAP_STATE_DISABLED 0 +#define CEC_ADAP_STATE_UNCONF 1 +#define CEC_ADAP_STATE_IDLE 2 +#define CEC_ADAP_STATE_TRANSMITTING 3 +#define CEC_ADAP_STATE_WAIT 4 +#define CEC_ADAP_STATE_RECEIVED 5 + +#define CEC_TX_QUEUE_SZ (4) +#define CEC_RX_QUEUE_SZ (4) +#define CEC_EV_QUEUE_SZ (16) + +struct cec_adapter { + struct module *owner; + const char *name; + struct cec_devnode devnode; + struct mutex lock; + struct rc_dev *rc; + + struct cec_data tx_queue[CEC_TX_QUEUE_SZ]; + u8 tx_qstart, tx_qcount; + + struct cec_msg rx_queue[CEC_RX_QUEUE_SZ]; + u8 rx_qstart, rx_qcount; + + struct cec_event ev_queue[CEC_EV_QUEUE_SZ]; + u8 ev_qstart, ev_qcount; + + cec_recv_notify recv_notifier; + struct task_struct *kthread_config; + + struct task_struct *kthread; + wait_queue_head_t kthread_waitq; + wait_queue_head_t waitq; + + u8 state; + u32 capabilities; + u16 phys_addr; + u8 version; + u8 num_log_addrs; + u8 key_passthrough; + u8 prim_device[CEC_MAX_LOG_ADDRS]; + u8 log_addr_type[CEC_MAX_LOG_ADDRS]; + u8 log_addr[CEC_MAX_LOG_ADDRS]; + + char input_name[32]; + char input_phys[32]; + char input_drv[32]; + + int (*adap_enable)(struct cec_adapter *adap, bool enable); + int (*adap_log_addr)(struct cec_adapter *adap, u8 logical_addr); + int (*adap_transmit)(struct cec_adapter *adap, struct cec_msg *msg); + void (*adap_transmit_timed_out)(struct cec_adapter *adap); + + void (*claimed_log_addr)(struct cec_adapter *adap, u8 idx); + int (*received)(struct cec_adapter *adap, struct cec_msg *msg); +}; + +#define to_cec_adapter(node) container_of(node, struct cec_adapter, devnode) + +int cec_create_adapter(struct cec_adapter *adap, const char *name, u32 caps); +void cec_delete_adapter(struct cec_adapter *adap); +int cec_transmit_msg(struct cec_adapter *adap, struct cec_data *data, bool block); +int cec_receive_msg(struct cec_adapter *adap, struct cec_msg *msg, bool block); +void cec_post_event(struct cec_adapter *adap, u32 event); +int cec_claim_log_addrs(struct cec_adapter *adap, struct cec_log_addrs *log_addrs, bool block); +int cec_enable(struct cec_adapter *adap, bool enable); + +/* Called by the adapter */ +void cec_transmit_done(struct cec_adapter *adap, u32 status); +void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg); + +#endif /* _CEC_DEVNODE_H */ diff --git a/include/uapi/linux/cec.h b/include/uapi/linux/cec.h new file mode 100644 index 00000000000000..e2ea1904417b4f --- /dev/null +++ b/include/uapi/linux/cec.h @@ -0,0 +1,276 @@ +#ifndef _CEC_H +#define _CEC_H + +#include + +struct cec_msg { + __u32 len; + __u8 msg[16]; + __u32 status; + /* If non-zero, then wait for a reply with this opcode. + If there was an error when sending the msg or FeatureAbort + was returned, then reply is set to 0. + If reply is non-zero upon return, then len/msg are set to + the received message. + If reply is zero upon return and status has the CEC_TX_STATUS_FEATURE_ABORT + bit set, then len/msg are set to the received feature abort message. + If reply is zero upon return and status has the CEC_TX_STATUS_REPLY_TIMEOUT + bit set, then no reply was seen at all. + This field is ignored with CEC_RECEIVE. + If reply is non-zero for CEC_TRANSMIT and the message is a broadcast, + then -EINVAL is returned. + if reply is non-zero, then timeout is set to 1000 (the required maximum + response time). + */ + __u8 reply; + /* timeout (in ms) is used to timeout CEC_RECEIVE. + Set to 0 if you want to wait forever. */ + __u32 timeout; + struct timespec ts; +}; + +static inline __u8 cec_msg_initiator(const struct cec_msg *msg) +{ + return msg->msg[0] >> 4; +} + +static inline __u8 cec_msg_destination(const struct cec_msg *msg) +{ + return msg->msg[0] & 0xf; +} + +static inline bool cec_msg_is_broadcast(const struct cec_msg *msg) +{ + return (msg->msg[0] & 0xf) == 0xf; +} + +/* cec status field */ +#define CEC_TX_STATUS_OK (0) +#define CEC_TX_STATUS_ARB_LOST (1 << 0) +#define CEC_TX_STATUS_RETRY_TIMEOUT (1 << 1) +#define CEC_TX_STATUS_FEATURE_ABORT (1 << 2) +#define CEC_TX_STATUS_REPLY_TIMEOUT (1 << 3) +#define CEC_RX_STATUS_READY (0) + +#define CEC_LOG_ADDR_INVALID 0xff + +/* The maximum number of logical addresses one device can be assigned to. + * The CEC 2.0 spec allows for only 2 logical addresses at the moment. The + * Analog Devices CEC hardware supports 3. So let's go wild and go for 4. */ +#define CEC_MAX_LOG_ADDRS 4 + +/* The "Primary Device Type" */ +#define CEC_PRIM_DEVTYPE_TV 0 +#define CEC_PRIM_DEVTYPE_RECORD 1 +#define CEC_PRIM_DEVTYPE_TUNER 3 +#define CEC_PRIM_DEVTYPE_PLAYBACK 4 +#define CEC_PRIM_DEVTYPE_AUDIOSYSTEM 5 +#define CEC_PRIM_DEVTYPE_SWITCH 6 +#define CEC_PRIM_DEVTYPE_VIDEOPROC 7 + +/* The "All Device Types" flags (CEC 2.0) */ +#define CEC_FL_ALL_DEVTYPE_TV (1 << 7) +#define CEC_FL_ALL_DEVTYPE_RECORD (1 << 6) +#define CEC_FL_ALL_DEVTYPE_TUNER (1 << 5) +#define CEC_FL_ALL_DEVTYPE_PLAYBACK (1 << 4) +#define CEC_FL_ALL_DEVTYPE_AUDIOSYSTEM (1 << 3) +#define CEC_FL_ALL_DEVTYPE_SWITCH (1 << 2) +/* And if you wondering what happened to VIDEOPROC devices: those should + * be mapped to a SWITCH. */ + +/* The logical address types that the CEC device wants to claim */ +#define CEC_LOG_ADDR_TYPE_TV 0 +#define CEC_LOG_ADDR_TYPE_RECORD 1 +#define CEC_LOG_ADDR_TYPE_TUNER 2 +#define CEC_LOG_ADDR_TYPE_PLAYBACK 3 +#define CEC_LOG_ADDR_TYPE_AUDIOSYSTEM 4 +#define CEC_LOG_ADDR_TYPE_SPECIFIC 5 +#define CEC_LOG_ADDR_TYPE_UNREGISTERED 6 +/* Switches should use UNREGISTERED. + * Video processors should use SPECIFIC. */ + +/* The CEC version */ +#define CEC_VERSION_1_4B 5 +#define CEC_VERSION_2_0 6 + +struct cec_event { + __u32 event; + struct timespec ts; +}; + +/* Userspace has to configure the adapter state (enable/disable) */ +#define CEC_CAP_STATE (1 << 0) +/* Userspace has to configure the physical address */ +#define CEC_CAP_PHYS_ADDR (1 << 1) +/* Userspace has to configure the logical addresses */ +#define CEC_CAP_LOG_ADDRS (1 << 2) +/* Userspace can transmit messages */ +#define CEC_CAP_TRANSMIT (1 << 3) +/* Userspace can receive messages */ +#define CEC_CAP_RECEIVE (1 << 4) + +struct cec_caps { + __u32 available_log_addrs; + __u32 capabilities; +}; + +struct cec_log_addrs { + __u8 cec_version; + __u8 num_log_addrs; + __u8 primary_device_type[CEC_MAX_LOG_ADDRS]; + __u8 log_addr_type[CEC_MAX_LOG_ADDRS]; + __u8 log_addr[CEC_MAX_LOG_ADDRS]; + + /* CEC 2.0 */ + __u8 all_device_types; + __u8 features[CEC_MAX_LOG_ADDRS][12]; +}; + +/* Commands */ + +/* One Touch Play Feature */ +#define CEC_OP_ACTIVE_SOURCE 0x82 +#define CEC_OP_IMAGE_VIEW_ON 0x04 +#define CEC_OP_TEXT_VIEW_ON 0x0d + +/* Routing Control Feature */ +#define CEC_OP_ACTIVE_SOURCE 0x82 +#define CEC_OP_INACTIVE_SOURCE 0x9d +#define CEC_OP_REQUEST_ACTIVE_SOURCE 0x85 +#define CEC_OP_ROUTING_CHANGE 0x80 +#define CEC_OP_ROUTING_INFORMATION 0x81 +#define CEC_OP_SET_STREAM_PATH 0x86 + +/* Standby Feature */ +#define CEC_OP_STANDBY 0x36 + +/* One Touch Record Feature */ +#define CEC_OP_RECORD_OFF 0x0b +#define CEC_OP_RECORD_ON 0x09 +#define CEC_OP_RECORD_STATUS 0x0a +#define CEC_OP_RECORD_TV_SCREEN 0x0f + +/* Timer Programming Feature */ +#define CEC_OP_CLEAR_ANALOGUE_TIMER 0x33 +#define CEC_OP_CLEAR_DIGITAL_TIMER 0x99 +#define CEC_OP_CLEAR_EXT_TIMER 0xa1 +#define CEC_OP_SET_ANALOGUE_TIMER 0x34 +#define CEC_OP_SET_DIGITAL_TIMER 0x97 +#define CEC_OP_SET_EXT_TIMER 0xa2 +#define CEC_OP_SET_EXT_PROGRAM_TIMER 0x67 +#define CEC_OP_TIMER_CLEARED_STATUS 0x43 +#define CEC_OP_TIMER_STATUS 0x35 + +/* System Information Feature */ +#define CEC_OP_CEC_VERSION 0x9e +#define CEC_OP_GET_CEC_VERSION 0x9f +#define CEC_OP_GIVE_PHYSICAL_ADDR 0x83 +#define CEC_OP_GET_MENU_LANGUAGE 0x91 +#define CEC_OP_REPORT_PHYSICAL_ADDR 0x84 +#define CEC_OP_SET_MENU_LANGUAGE 0x32 + +/* Deck Control Feature */ +#define CEC_OP_DECK_CONTROL 0x42 +#define CEC_OP_DECK_STATUS 0x1b +#define CEC_OP_GIVE_DECK_STATUS 0x1a +#define CEC_OP_PLAY 0x41 + +/* Tuner Control Feature */ +#define CEC_OP_GIVE_TUNER_DEVICE_STATUS 0x08 +#define CEC_OP_SELECT_ANALOGUE_SERVICE 0x92 +#define CEC_OP_SELECT_DIGITAL_SERVICE 0x93 +#define CEC_OP_TUNER_DEVICE_STATUS 0x07 +#define CEC_OP_TUNER_STEP_DECREMENT 0x06 +#define CEC_OP_TUNER_STEP_INCREMENT 0x05 + +/* Vendor Specific Commands Feature */ +#define CEC_OP_CEC_VERSION 0x9e +#define CEC_OP_DEVICE_VENDOR_ID 0x87 +#define CEC_OP_GET_CEC_VERSION 0x9f +#define CEC_OP_GIVE_DEVICE_VENDOR_ID 0x8c +#define CEC_OP_VENDOR_COMMAND 0x89 +#define CEC_OP_VENDOR_COMMAND_WITH_ID 0xa0 +#define CEC_OP_VENDOR_REMOTE_BUTTON_DOWN 0x8a +#define CEC_OP_VENDOR_REMOTE_BUTTON_UP 0x8b + +/* OSD Display Feature */ +#define CEC_OP_SET_OSD_STRING 0x64 + +/* Device OSD Transfer Feature */ +#define CEC_OP_GIVE_OSD_NAME 0x46 +#define CEC_OP_SET_OSD_NAME 0x47 + +/* Device Menu Control Feature */ +#define CEC_OP_MENU_REQUEST 0x8d +#define CEC_OP_MENU_STATUS 0x8e +#define CEC_OP_USER_CONTROL_PRESSED 0x44 +#define CEC_OP_USER_CONTROL_RELEASED 0x45 + +/* Power Status Feature */ +#define CEC_OP_GIVE_DEVICE_POWER_STATUS 0x8f +#define CEC_OP_REPORT_POWER_STATUS 0x90 +#define CEC_OP_FEATURE_ABORT 0x00 +#define CEC_OP_ABORT 0xff + +/* System Audio Control Feature */ +#define CEC_OP_GIVE_AUDIO_STATUS 0x71 +#define CEC_OP_GIVE_SYSTEM_AUDIO_MODE_STATUS 0x7d +#define CEC_OP_REPORT_AUDIO_STATUS 0x7a +#define CEC_OP_SET_SYSTEM_AUDIO_MODE 0x72 +#define CEC_OP_SYSTEM_AUDIO_MODE_REQUEST 0x70 +#define CEC_OP_SYSTEM_AUDIO_MODE_STATUS 0x7e + +/* Audio Rate Control Feature */ +#define CEC_OP_SET_AUDIO_RATE 0x9a + +/* ioctls */ + +#define CEC_EVENT_READY 1 +#define CEC_EVENT_DISCONNECT 2 + +/* issue a CEC command */ +#define CEC_G_CAPS _IOWR('a', 0, struct cec_caps) +#define CEC_TRANSMIT _IOWR('a', 1, struct cec_msg) +#define CEC_RECEIVE _IOWR('a', 2, struct cec_msg) + +/* + Configure the CEC adapter. It sets the device type and which + logical types it will try to claim. It will return which + logical addresses it could actually claim. + An error is returned if the adapter is disabled or if there + is no physical address assigned. + */ + +#define CEC_G_ADAP_LOG_ADDRS _IOR('a', 3, struct cec_log_addrs) +#define CEC_S_ADAP_LOG_ADDRS _IOWR('a', 4, struct cec_log_addrs) + +/* + Enable/disable the adapter. The Set state ioctl may not + be available if that is handled internally. + */ +#define CEC_G_ADAP_STATE _IOR('a', 5, __u32) +#define CEC_S_ADAP_STATE _IOW('a', 6, __u32) + +/* + phys_addr is either 0 (if this is the CEC root device) + or a valid physical address obtained from the sink's EDID + as read by this CEC device (if this is a source device) + or a physical address obtained and modified from a sink + EDID and used for a sink CEC device. + If nothing is connected, then phys_addr is 0xffff. + See HDMI 1.4b, section 8.7 (Physical Address). + + The Set ioctl may not be available if that is handled + internally. + */ +#define CEC_G_ADAP_PHYS_ADDR _IOR('a', 7, __u16) +#define CEC_S_ADAP_PHYS_ADDR _IOW('a', 8, __u16) + +#define CEC_G_EVENT _IOWR('a', 9, struct cec_event) + +#define CEC_G_KEY_PASSTHROUGH _IOR('a', 10, __u8) +#define CEC_S_KEY_PASSTHROUGH _IOW('a', 11, __u8) +#define CEC_KEY_PASSTHROUGH_DISABLE 0 +#define CEC_KEY_PASSTHROUGH_ENABLE 1 + +#endif From 44f9f9784e8c82d1d28bb19ea42ba45dbf76ff6f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 22 Jan 2015 17:04:36 +0100 Subject: [PATCH 681/788] v4l2-subdev: add cec ops. Add callbacks to the v4l2_subdev_video_ops. Signed-off-by: Hans Verkuil [k.debski@samsung.com: Merged changes from CEC Updates commit by Hans Verkuil] Signed-off-by: Kamil Debski --- include/media/v4l2-subdev.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 5860292d42ebdf..02070cc75d985b 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -40,6 +40,9 @@ #define V4L2_SUBDEV_IR_TX_NOTIFY _IOW('v', 1, u32) #define V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ 0x00000001 +#define V4L2_SUBDEV_CEC_TX_DONE _IOW('v', 2, u32) +#define V4L2_SUBDEV_CEC_RX_MSG _IOW('v', 3, struct cec_msg) + struct v4l2_device; struct v4l2_ctrl_handler; struct v4l2_event_subscription; @@ -48,6 +51,7 @@ struct v4l2_subdev; struct v4l2_subdev_fh; struct tuner_setup; struct v4l2_mbus_frame_desc; +struct cec_msg; /* decode_vbi_line */ struct v4l2_decode_vbi_line { @@ -356,6 +360,10 @@ struct v4l2_subdev_video_ops { const struct v4l2_mbus_config *cfg); int (*s_rx_buffer)(struct v4l2_subdev *sd, void *buf, unsigned int *size); + int (*cec_enable)(struct v4l2_subdev *sd, bool enable); + int (*cec_log_addr)(struct v4l2_subdev *sd, u8 logical_addr); + int (*cec_transmit)(struct v4l2_subdev *sd, struct cec_msg *msg); + void (*cec_transmit_timed_out)(struct v4l2_subdev *sd); }; /* From fd55a3e08a2b13eaeaee4687da4ca9d70990250d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 22 Jan 2015 17:04:37 +0100 Subject: [PATCH 682/788] adv7604: add cec support. Add CEC support ot the adv7604 driver. Signed-off-by: Hans Verkuil [k.debski@samsung.com: Merged changes from CEC Updates commit by Hans Verkuil] Signed-off-by: Kamil Debski --- drivers/media/i2c/adv7604.c | 182 ++++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index e43dd2e2a38a45..f0ea92942d743e 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -42,6 +42,7 @@ #include #include #include +#include static int debug; module_param(debug, int, 0644); @@ -158,6 +159,10 @@ struct adv7604_state { u16 spa_port_a[2]; struct v4l2_fract aspect_ratio; u32 rgb_quantization_range; + u8 cec_addr[3]; + u8 cec_valid_addrs; + bool cec_enabled_adap; + struct workqueue_struct *work_queues; struct delayed_work delayed_work_enable_hotplug; bool restart_stdi_once; @@ -1935,6 +1940,176 @@ static int adv7604_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, return 0; } +static void adv7604_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status) +{ + if ((cec_read(sd, 0x11) & 0x01) == 0) { + v4l2_dbg(1, debug, sd, "%s: tx raw: tx disabled\n", __func__); + return; + } + + if (tx_raw_status & 0x02) { + v4l2_dbg(1, debug, sd, "%s: tx raw: arbitration lost\n", __func__); + v4l2_subdev_notify(sd, V4L2_SUBDEV_CEC_TX_DONE, (void *)CEC_TX_STATUS_ARB_LOST); + return; + } + if (tx_raw_status & 0x04) { + v4l2_dbg(1, debug, sd, "%s: tx raw: retry failed\n", __func__); + v4l2_subdev_notify(sd, V4L2_SUBDEV_CEC_TX_DONE, (void *)CEC_TX_STATUS_RETRY_TIMEOUT); + return; + } + if (tx_raw_status & 0x01) { + v4l2_dbg(1, debug, sd, "%s: tx raw: ready ok\n", __func__); + v4l2_subdev_notify(sd, V4L2_SUBDEV_CEC_TX_DONE, (void *)CEC_TX_STATUS_OK); + return; + } +} + +static void adv7604_cec_isr(struct v4l2_subdev *sd, bool *handled) +{ + struct cec_msg msg; + u8 cec_irq; + + /* cec controller */ + cec_irq = io_read(sd, 0x4d) & 0x0f; + if (!cec_irq) + return; + + v4l2_dbg(1, debug, sd, "%s: cec: irq 0x%x\n", __func__, cec_irq); + adv7604_cec_tx_raw_status(sd, cec_irq); + if (cec_irq & 0x08) { + msg.len = cec_read(sd, 0x25) & 0x1f; + if (msg.len > 16) + msg.len = 16; + + if (msg.len) { + u8 i; + + for (i = 0; i < msg.len; i++) + msg.msg[i] = cec_read(sd, i + 0x15); + cec_write(sd, 0x26, 0x01); /* re-enable rx */ + v4l2_subdev_notify(sd, V4L2_SUBDEV_CEC_RX_MSG, &msg); + } + } + + /* note: the bit order is swapped between 0x4d and 0x4e */ + cec_irq = ((cec_irq & 0x08) >> 3) | ((cec_irq & 0x04) >> 1) | + ((cec_irq & 0x02) << 1) | ((cec_irq & 0x01) << 3); + io_write(sd, 0x4e, cec_irq); + + if (handled) + *handled = true; +} + +static int adv7604_cec_enable(struct v4l2_subdev *sd, bool enable) +{ + struct adv7604_state *state = to_state(sd); + + if (!state->cec_enabled_adap && enable) { + cec_write_and_or(sd, 0x2a, 0xfe, 0x01); /* power up cec */ + cec_write(sd, 0x2c, 0x01); /* cec soft reset */ + cec_write_and_or(sd, 0x11, 0xfe, 0); /* initially disable tx */ + /* enabled irqs: */ + /* tx: ready */ + /* tx: arbitration lost */ + /* tx: retry timeout */ + /* rx: ready */ + io_write_and_or(sd, 0x50, 0xf0, 0x0f); + cec_write(sd, 0x26, 0x01); /* enable rx */ + } else if (state->cec_enabled_adap && !enable) { + io_write_and_or(sd, 0x50, 0xf0, 0x00); /* disable cec interrupts */ + cec_write_and_or(sd, 0x27, 0x8f, 0x70); /* disable address mask 1-3 */ + cec_write_and_or(sd, 0x2a, 0xfe, 0x00); /* power down cec section */ + state->cec_valid_addrs = 0; + } + state->cec_enabled_adap = enable; + return 0; +} + +#define ADV7604_MAX_ADDRS (3) + +static int adv7604_cec_log_addr(struct v4l2_subdev *sd, u8 addr) +{ + struct adv7604_state *state = to_state(sd); + unsigned i, free_idx = ADV7604_MAX_ADDRS; + + if (!state->cec_enabled_adap) + return -EIO; + + for (i = 0; i < ADV7604_MAX_ADDRS; i++) { + bool is_valid = state->cec_valid_addrs & (1 << i); + + if (free_idx == ADV7604_MAX_ADDRS && !is_valid) + free_idx = i; + if (is_valid && state->cec_addr[i] == addr) + return 0; + } + if (i == ADV7604_MAX_ADDRS) { + i = free_idx; + if (i == ADV7604_MAX_ADDRS) + return -ENXIO; + } + state->cec_addr[i] = addr; + state->cec_valid_addrs |= 1 << i; + + switch (i) { + case 0: + /* enable address mask 0 */ + cec_write_and_or(sd, 0x27, 0xef, 0x10); + /* set address for mask 0 */ + cec_write_and_or(sd, 0x28, 0xf0, addr); + break; + case 1: + /* enable address mask 1 */ + cec_write_and_or(sd, 0x27, 0xdf, 0x20); + /* set address for mask 1 */ + cec_write_and_or(sd, 0x28, 0x0f, addr << 4); + break; + case 2: + /* enable address mask 2 */ + cec_write_and_or(sd, 0x27, 0xbf, 0x40); + /* set address for mask 1 */ + cec_write_and_or(sd, 0x29, 0xf0, addr); + break; + } + return 0; +} + +static int adv7604_cec_transmit(struct v4l2_subdev *sd, struct cec_msg *msg) +{ + u8 len = msg->len; + unsigned i; + + if (len == 1) + cec_write_and_or(sd, 0x12, 0xf8, 1); /* allow for one retry for polling */ + else + cec_write_and_or(sd, 0x12, 0xf8, 3); /* allow for three retries */ + + if (len > 16) { + v4l2_err(sd, "%s: len exceeded 16 (%d)\n", __func__, len); + return -EINVAL; + } + + /* write data */ + for (i = 0; i < len; i++) + cec_write(sd, i, msg->msg[i]); + + /* set length (data + header) */ + cec_write(sd, 0x10, len); + /* start transmit, enable tx */ + cec_write(sd, 0x11, 0x01); + /* For some reason sometimes the + * transmit won't start. + * Doing it twice seems to help ? + */ + cec_write(sd, 0x11, 0x01); + return 0; +} + +static void adv7604_cec_transmit_timed_out(struct v4l2_subdev *sd) +{ + cec_write_and_or(sd, 0x11, 0xfe, 0); /* disable tx */ +} + static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled) { struct adv7604_state *state = to_state(sd); @@ -1980,6 +2155,9 @@ static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled) *handled = true; } + /* cec */ + adv7604_cec_isr(sd, handled); + /* tx 5v detect */ tx_5v = io_read(sd, 0x70) & info->cable_det_mask; if (tx_5v) { @@ -2374,6 +2552,10 @@ static const struct v4l2_subdev_video_ops adv7604_video_ops = { .s_dv_timings = adv7604_s_dv_timings, .g_dv_timings = adv7604_g_dv_timings, .query_dv_timings = adv7604_query_dv_timings, + .cec_enable = adv7604_cec_enable, + .cec_log_addr = adv7604_cec_log_addr, + .cec_transmit = adv7604_cec_transmit, + .cec_transmit_timed_out = adv7604_cec_transmit_timed_out, }; static const struct v4l2_subdev_pad_ops adv7604_pad_ops = { From 4daee8b1b75e89d03158138179277e5c5cab002c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 22 Jan 2015 17:04:38 +0100 Subject: [PATCH 683/788] adv7511: add cec support. Add CEC support to the adv7511 driver. Signed-off-by: Hans Verkuil [k.debski@samsung.com: Merged changes from CEC Updates commit by Hans Verkuil] Signed-off-by: Kamil Debski --- drivers/media/i2c/adv7511.c | 325 +++++++++++++++++++++++++++++++++++- include/media/adv7511.h | 6 +- 2 files changed, 323 insertions(+), 8 deletions(-) diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c index 81736aaf0f311c..63ec6c1171efe1 100644 --- a/drivers/media/i2c/adv7511.c +++ b/drivers/media/i2c/adv7511.c @@ -33,6 +33,7 @@ #include #include #include +#include static int debug; module_param(debug, int, 0644); @@ -91,6 +92,12 @@ struct adv7511_state { int chip_revision; uint8_t i2c_edid_addr; uint8_t i2c_cec_addr; + + struct i2c_client *i2c_cec; + u8 cec_addr[3]; + u8 cec_valid_addrs; + bool cec_enabled_adap; + /* Is the adv7511 powered on? */ bool power_on; /* Did we receive hotplug and rx-sense signals? */ @@ -222,7 +229,7 @@ static int adv_smbus_read_i2c_block_data(struct i2c_client *client, return ret; } -static inline void adv7511_edid_rd(struct v4l2_subdev *sd, uint16_t len, uint8_t *buf) +static void adv7511_edid_rd(struct v4l2_subdev *sd, uint16_t len, uint8_t *buf) { struct adv7511_state *state = get_adv7511_state(sd); int i; @@ -237,6 +244,33 @@ static inline void adv7511_edid_rd(struct v4l2_subdev *sd, uint16_t len, uint8_t v4l2_err(sd, "%s: i2c read error\n", __func__); } +static inline int cec_read(struct v4l2_subdev *sd, u8 reg) +{ + struct adv7511_state *state = get_adv7511_state(sd); + + return i2c_smbus_read_byte_data(state->i2c_cec, reg); +} + +static int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct adv7511_state *state = get_adv7511_state(sd); + int ret; + int i; + + for (i = 0; i < 3; i++) { + ret = i2c_smbus_write_byte_data(state->i2c_cec, reg, val); + if (ret == 0) + return 0; + } + v4l2_err(sd, "%s: I2C Write Problem\n", __func__); + return ret; +} + +static inline int cec_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) +{ + return cec_write(sd, reg, (cec_read(sd, reg) & mask) | val); +} + static inline bool adv7511_have_hotplug(struct v4l2_subdev *sd) { return adv7511_rd(sd, 0x42) & MASK_ADV7511_HPD_DETECT; @@ -381,16 +415,28 @@ static const struct v4l2_ctrl_ops adv7511_ctrl_ops = { #ifdef CONFIG_VIDEO_ADV_DEBUG static void adv7511_inv_register(struct v4l2_subdev *sd) { + struct adv7511_state *state = get_adv7511_state(sd); + v4l2_info(sd, "0x000-0x0ff: Main Map\n"); + if (state->i2c_cec) + v4l2_info(sd, "0x100-0x1ff: CEC Map\n"); } static int adv7511_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { + struct adv7511_state *state = get_adv7511_state(sd); + reg->size = 1; switch (reg->reg >> 8) { case 0: reg->val = adv7511_rd(sd, reg->reg & 0xff); break; + case 1: + if (state->i2c_cec) { + reg->val = cec_read(sd, reg->reg & 0xff); + break; + } + /* fall through */ default: v4l2_info(sd, "Register %03llx not supported\n", reg->reg); adv7511_inv_register(sd); @@ -401,10 +447,18 @@ static int adv7511_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register * static int adv7511_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) { + struct adv7511_state *state = get_adv7511_state(sd); + switch (reg->reg >> 8) { case 0: adv7511_wr(sd, reg->reg & 0xff, reg->val & 0xff); break; + case 1: + if (state->i2c_cec) { + cec_write(sd, reg->reg & 0xff, reg->val & 0xff); + break; + } + /* fall through */ default: v4l2_info(sd, "Register %03llx not supported\n", reg->reg); adv7511_inv_register(sd); @@ -418,6 +472,7 @@ static int adv7511_log_status(struct v4l2_subdev *sd) { struct adv7511_state *state = get_adv7511_state(sd); struct adv7511_state_edid *edid = &state->edid; + int i; static const char * const states[] = { "in reset", @@ -486,7 +541,20 @@ static int adv7511_log_status(struct v4l2_subdev *sd) else v4l2_info(sd, "no timings set\n"); v4l2_info(sd, "i2c edid addr: 0x%x\n", state->i2c_edid_addr); + + if (state->i2c_cec == NULL) + return 0; + v4l2_info(sd, "i2c cec addr: 0x%x\n", state->i2c_cec_addr); + + if (cec_read(sd, 0x4e) & 0x01) { + v4l2_info(sd, "cec: enabled\n"); + for (i = 0; i < 3; i++) + if (state->cec_valid_addrs & (1 << i)) + v4l2_info(sd, "cec device %d: addr %d\n", i, state->cec_addr[i]); + } else { + v4l2_info(sd, "cec: disabled\n"); + } return 0; } @@ -542,15 +610,132 @@ static int adv7511_s_power(struct v4l2_subdev *sd, int on) return true; } +static int adv7511_cec_enable(struct v4l2_subdev *sd, bool enable) +{ + struct adv7511_state *state = get_adv7511_state(sd); + + if (!state->cec_enabled_adap && enable) { + cec_write_and_or(sd, 0x4e, 0xfc, 0x01); /* power up cec section */ + cec_write(sd, 0x4a, 0x07); /* legacy mode and clear all rx buffers */ + cec_write(sd, 0x4a, 0); + cec_write_and_or(sd, 0x11, 0xfe, 0); /* initially disable tx */ + /* enabled irqs: */ + /* tx: ready */ + /* tx: arbitration lost */ + /* tx: retry timeout */ + /* rx: ready 1 */ + adv7511_wr_and_or(sd, 0x95, 0xc0, 0x39); + } else if (state->cec_enabled_adap && !enable) { + adv7511_wr_and_or(sd, 0x95, 0xc0, 0x00); + cec_write_and_or(sd, 0x4b, 0x8f, 0x00); /* disable address mask 1-3 */ + cec_write_and_or(sd, 0x4e, 0xfc, 0x00); /* power down cec section */ + state->cec_valid_addrs = 0; + } + state->cec_enabled_adap = enable; + return 0; +} + +#define ADV7511_MAX_ADDRS (3) + +static int adv7511_cec_log_addr(struct v4l2_subdev *sd, u8 addr) +{ + struct adv7511_state *state = get_adv7511_state(sd); + unsigned i, free_idx = ADV7511_MAX_ADDRS; + + if (!state->cec_enabled_adap) + return -EIO; + + for (i = 0; i < ADV7511_MAX_ADDRS; i++) { + bool is_valid = state->cec_valid_addrs & (1 << i); + + if (free_idx == ADV7511_MAX_ADDRS && !is_valid) + free_idx = i; + if (is_valid && state->cec_addr[i] == addr) + return 0; + } + if (i == ADV7511_MAX_ADDRS) { + i = free_idx; + if (i == ADV7511_MAX_ADDRS) + return -ENXIO; + } + state->cec_addr[i] = addr; + state->cec_valid_addrs |= 1 << i; + + switch (i) { + case 0: + /* enable address mask 0 */ + cec_write_and_or(sd, 0x4b, 0xef, 0x10); + /* set address for mask 0 */ + cec_write_and_or(sd, 0x4c, 0xf0, addr); + break; + case 1: + /* enable address mask 1 */ + cec_write_and_or(sd, 0x4b, 0xdf, 0x20); + /* set address for mask 1 */ + cec_write_and_or(sd, 0x4c, 0x0f, addr << 4); + break; + case 2: + /* enable address mask 2 */ + cec_write_and_or(sd, 0x4b, 0xbf, 0x40); + /* set address for mask 1 */ + cec_write_and_or(sd, 0x4d, 0xf0, addr); + break; + } + return 0; +} + +static int adv7511_cec_transmit(struct v4l2_subdev *sd, struct cec_msg *msg) +{ + u8 len = msg->len; + unsigned i; + + v4l2_dbg(1, debug, sd, "%s: len %d\n", __func__, len); + + if (len > 16) { + v4l2_err(sd, "%s: len exceeded 16 (%d)\n", __func__, len); + return -EINVAL; + } + + /* blocking, clear cec tx irq status */ + adv7511_wr_and_or(sd, 0x97, 0xc7, 0x38); + + /* write data */ + for (i = 0; i < len; i++) + cec_write(sd, i, msg->msg[i]); + + /* set length (data + header) */ + cec_write(sd, 0x10, len); + /* start transmit, enable tx */ + cec_write(sd, 0x11, 0x01); + return 0; +} + +static void adv7511_cec_transmit_timed_out(struct v4l2_subdev *sd) +{ + cec_write_and_or(sd, 0x11, 0xfe, 0); /* disable tx */ +} + /* Enable interrupts */ static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable) { + struct adv7511_state *state = get_adv7511_state(sd); uint8_t irqs = MASK_ADV7511_HPD_INT | MASK_ADV7511_MSEN_INT; uint8_t irqs_rd; int retries = 100; v4l2_dbg(2, debug, sd, "%s: %s\n", __func__, enable ? "enable" : "disable"); + if (state->i2c_cec) { + /* + * Enabled CEC irqs: + * tx: ready + * tx: arbitration lost + * tx: retry timeout + * rx: ready 1 + */ + adv7511_wr_and_or(sd, 0x95, 0xc0, enable ? 0x39 : 0); + } + /* The datasheet says that the EDID ready interrupt should be disabled if there is no hotplug. */ if (!enable) @@ -576,24 +761,76 @@ static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable) v4l2_err(sd, "Could not set interrupts: hw failure?\n"); } +static void adv_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status) +{ + if ((cec_read(sd, 0x11) & 0x01) == 0) { + v4l2_dbg(1, debug, sd, "%s: tx raw: tx disabled\n", __func__); + return; + } + + if (tx_raw_status & 0x10) { + v4l2_dbg(1, debug, sd, "%s: tx raw: arbitration lost\n", __func__); + v4l2_subdev_notify(sd, V4L2_SUBDEV_CEC_TX_DONE, (void *)CEC_TX_STATUS_ARB_LOST); + return; + } + if (tx_raw_status & 0x08) { + v4l2_dbg(1, debug, sd, "%s: tx raw: retry failed\n", __func__); + v4l2_subdev_notify(sd, V4L2_SUBDEV_CEC_TX_DONE, (void *)CEC_TX_STATUS_RETRY_TIMEOUT); + return; + } + if (tx_raw_status & 0x20) { + v4l2_dbg(1, debug, sd, "%s: tx raw: ready ok\n", __func__); + v4l2_subdev_notify(sd, V4L2_SUBDEV_CEC_TX_DONE, (void *)CEC_TX_STATUS_OK); + return; + } +} + /* Interrupt handler */ static int adv7511_isr(struct v4l2_subdev *sd, u32 status, bool *handled) { uint8_t irq_status; + uint8_t cec_irq; /* disable interrupts to prevent a race condition */ adv7511_set_isr(sd, false); irq_status = adv7511_rd(sd, 0x96); + cec_irq = adv7511_rd(sd, 0x97); /* clear detected interrupts */ adv7511_wr(sd, 0x96, irq_status); + adv7511_wr(sd, 0x97, cec_irq); - v4l2_dbg(1, debug, sd, "%s: irq 0x%x\n", __func__, irq_status); + v4l2_dbg(1, debug, sd, "%s: irq 0x%x, cec-irq 0x%x\n", __func__, irq_status, cec_irq); if (irq_status & (MASK_ADV7511_HPD_INT | MASK_ADV7511_MSEN_INT)) adv7511_check_monitor_present_status(sd); if (irq_status & MASK_ADV7511_EDID_RDY_INT) adv7511_check_edid_status(sd); + if (cec_irq & 0x38) + adv_cec_tx_raw_status(sd, cec_irq); + + if (cec_irq & 1) { + struct cec_msg msg; + + msg.len = cec_read(sd, 0x25) & 0x1f; + + v4l2_dbg(1, debug, sd, "%s: cec msg len %d\n", __func__, msg.len); + + if (msg.len > 16) + msg.len = 16; + + if (msg.len) { + u8 i; + + for (i = 0; i < msg.len; i++) + msg.msg[i] = cec_read(sd, i + 0x15); + + cec_write(sd, 0x4a, 1); /* toggle to re-enable rx 1 */ + cec_write(sd, 0x4a, 0); + v4l2_subdev_notify(sd, V4L2_SUBDEV_CEC_RX_MSG, &msg); + } + } + /* enable interrupts */ adv7511_set_isr(sd, true); @@ -697,6 +934,10 @@ static const struct v4l2_subdev_video_ops adv7511_video_ops = { .s_stream = adv7511_s_stream, .s_dv_timings = adv7511_s_dv_timings, .g_dv_timings = adv7511_g_dv_timings, + .cec_enable = adv7511_cec_enable, + .cec_log_addr = adv7511_cec_log_addr, + .cec_transmit = adv7511_cec_transmit, + .cec_transmit_timed_out = adv7511_cec_transmit_timed_out, }; /* ------------------------------ AUDIO OPS ------------------------------ */ @@ -1078,6 +1319,7 @@ static void adv7511_edid_handler(struct work_struct *work) /* We failed to read the EDID, so send an event for this. */ ed.present = false; ed.segment = adv7511_rd(sd, 0xc4); + ed.phys_addr = 0xffff; v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed); v4l2_dbg(1, debug, sd, "%s: no edid found\n", __func__); } @@ -1218,10 +1460,38 @@ static bool edid_verify_header(struct v4l2_subdev *sd, u32 segment) return !memcmp(data, hdmi_header, sizeof(hdmi_header)); } +static int get_edid_spa_location(const u8 *edid) +{ + u8 d; + + if ((edid[0x7e] != 1) || + (edid[0x80] != 0x02) || + (edid[0x81] != 0x03)) { + return -1; + } + + /* search Vendor Specific Data Block (tag 3) */ + d = edid[0x82] & 0x7f; + if (d > 4) { + int i = 0x84; + int end = 0x80 + d; + do { + u8 tag = edid[i] >> 5; + u8 len = edid[i] & 0x1f; + + if ((tag == 3) && (len >= 5)) + return i + 4; + i += len + 1; + } while (i < end); + } + return -1; +} + static bool adv7511_check_edid_status(struct v4l2_subdev *sd) { struct adv7511_state *state = get_adv7511_state(sd); uint8_t edidRdy = adv7511_rd(sd, 0xc5); + int offset; v4l2_dbg(1, debug, sd, "%s: edid ready (retries: %d)\n", __func__, EDID_MAX_RETRIES - state->edid.read_retries); @@ -1267,6 +1537,12 @@ static bool adv7511_check_edid_status(struct v4l2_subdev *sd) v4l2_dbg(1, debug, sd, "%s: edid complete with %d segment(s)\n", __func__, state->edid.segments); state->edid.complete = true; + offset = get_edid_spa_location(state->edid.data); + if (offset > 0) + ed.phys_addr = (state->edid.data[offset] << 8) | + state->edid.data[offset + 1]; + else + ed.phys_addr = 0xffff; /* report when we have all segments but report only for segment 0 @@ -1288,11 +1564,14 @@ static void adv7511_init_setup(struct v4l2_subdev *sd) { struct adv7511_state *state = get_adv7511_state(sd); struct adv7511_state_edid *edid = &state->edid; + u32 cec_clk = state->pdata.cec_clk; + u8 ratio; v4l2_dbg(1, debug, sd, "%s\n", __func__); /* clear all interrupts */ adv7511_wr(sd, 0x96, 0xff); + adv7511_wr(sd, 0x97, 0xff); /* * Stop HPD from resetting a lot of registers. * It might leave the chip in a partly un-initialized state, @@ -1304,6 +1583,24 @@ static void adv7511_init_setup(struct v4l2_subdev *sd) adv7511_set_isr(sd, false); adv7511_s_stream(sd, false); adv7511_s_audio_stream(sd, false); + + if (state->i2c_cec == NULL) + return; + + v4l2_dbg(1, debug, sd, "%s: cec_clk %d\n", __func__, cec_clk); + + /* cec soft reset */ + cec_write(sd, 0x50, 0x01); + cec_write(sd, 0x50, 0x00); + + /* legacy mode */ + cec_write(sd, 0x4a, 0x00); + + if (cec_clk % 750000 != 0) + v4l2_err(sd, "%s: cec_clk %d, not multiple of 750 Khz\n", __func__, cec_clk); + + ratio = (cec_clk / 750000) - 1; + cec_write(sd, 0x4e, ratio << 2); } static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *id) @@ -1391,7 +1688,24 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id * goto err_entity; } - adv7511_wr(sd, 0xe2, 0x01); /* power down cec section */ + adv7511_wr(sd, 0xe1, state->i2c_cec_addr); + if (state->pdata.cec_clk < 3000000 || state->pdata.cec_clk > 100000000) { + v4l2_err(sd, "%s: cec_clk %u outside range, disabling cec\n", + __func__, state->pdata.cec_clk); + state->pdata.cec_clk = 0; + } + + if (state->pdata.cec_clk) { + state->i2c_cec = i2c_new_dummy(client->adapter, (state->i2c_cec_addr>>1)); + if (state->i2c_cec == NULL) { + v4l2_err(sd, "failed to register cec i2c client\n"); + goto err_unreg_edid; + } + adv7511_wr(sd, 0xe2, 0x00); /* power up cec section */ + } else { + adv7511_wr(sd, 0xe2, 0x01); /* power down cec section */ + } + state->work_queue = create_singlethread_workqueue(sd->name); if (state->work_queue == NULL) { v4l2_err(sd, "could not create workqueue\n"); @@ -1410,6 +1724,9 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id * return 0; err_unreg_cec: + if (state->i2c_cec) + i2c_unregister_device(state->i2c_cec); +err_unreg_edid: i2c_unregister_device(state->i2c_edid); err_entity: media_entity_cleanup(&sd->entity); @@ -1433,6 +1750,8 @@ static int adv7511_remove(struct i2c_client *client) adv7511_init_setup(sd); cancel_delayed_work(&state->edid_handler); i2c_unregister_device(state->i2c_edid); + if (state->i2c_cec) + i2c_unregister_device(state->i2c_cec); destroy_workqueue(state->work_queue); v4l2_device_unregister_subdev(sd); media_entity_cleanup(&sd->entity); diff --git a/include/media/adv7511.h b/include/media/adv7511.h index bb78bed9a5b84d..c971b525bc5156 100644 --- a/include/media/adv7511.h +++ b/include/media/adv7511.h @@ -32,11 +32,7 @@ struct adv7511_monitor_detect { struct adv7511_edid_detect { int present; int segment; -}; - -struct adv7511_cec_arg { - void *arg; - u32 f_flags; + uint16_t phys_addr; }; struct adv7511_platform_data { From 3ccbe74c8feddba0eee992e4ce02a18b4bccbff1 Mon Sep 17 00:00:00 2001 From: Kamil Debski Date: Thu, 22 Jan 2015 17:04:39 +0100 Subject: [PATCH 684/788] s5p-cec: Add s5p-cec driver Add CEC interface driver present in the Samsung Exynos range of SoCs. The following files were based on work by SangPil Moon: - exynos_hdmi_cec.h - exynos_hdmi_cecctl.c Signed-off-by: Kamil Debski --- drivers/media/platform/Kconfig | 7 + drivers/media/platform/Makefile | 1 + drivers/media/platform/s5p-cec/Makefile | 4 + .../media/platform/s5p-cec/exynos_hdmi_cec.h | 37 +++ .../platform/s5p-cec/exynos_hdmi_cecctrl.c | 208 +++++++++++++ drivers/media/platform/s5p-cec/regs-cec.h | 96 ++++++ drivers/media/platform/s5p-cec/s5p_cec.c | 290 ++++++++++++++++++ drivers/media/platform/s5p-cec/s5p_cec.h | 113 +++++++ 8 files changed, 756 insertions(+) create mode 100644 drivers/media/platform/s5p-cec/Makefile create mode 100644 drivers/media/platform/s5p-cec/exynos_hdmi_cec.h create mode 100644 drivers/media/platform/s5p-cec/exynos_hdmi_cecctrl.c create mode 100644 drivers/media/platform/s5p-cec/regs-cec.h create mode 100644 drivers/media/platform/s5p-cec/s5p_cec.c create mode 100644 drivers/media/platform/s5p-cec/s5p_cec.h diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 6a1334be75442b..ef823029ad0220 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -153,6 +153,13 @@ config VIDEO_MEM2MEM_DEINTERLACE help Generic deinterlacing V4L2 driver. +config VIDEO_SAMSUNG_S5P_CEC + tristate "Samsung S5P CEC driver" + depends on VIDEO_DEV && VIDEO_V4L2 && (PLAT_S5P || ARCH_EXYNOS) + default n + ---help--- + This is a v4l2 driver for Samsung S5P HDMI CEC interface. + config VIDEO_SAMSUNG_S5P_G2D tristate "Samsung S5P and EXYNOS4 G2D 2d graphics accelerator driver" depends on VIDEO_DEV && VIDEO_V4L2 diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index a49936b8ce8a5e..32583edff14769 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_VIDEO_MEM2MEM_DEINTERLACE) += m2m-deinterlace.o obj-$(CONFIG_VIDEO_S3C_CAMIF) += s3c-camif/ obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS4_IS) += exynos4-is/ +obj-$(CONFIG_VIDEO_SAMSUNG_S5P_CEC) += s5p-cec/ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg/ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) += s5p-mfc/ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_TV) += s5p-tv/ diff --git a/drivers/media/platform/s5p-cec/Makefile b/drivers/media/platform/s5p-cec/Makefile new file mode 100644 index 00000000000000..7f842266c67945 --- /dev/null +++ b/drivers/media/platform/s5p-cec/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_VIDEO_SAMSUNG_S5P_CEC) += s5p-cec.o +s5p-cec-y += s5p_cec.o exynos_hdmi_cecctrl.o + + diff --git a/drivers/media/platform/s5p-cec/exynos_hdmi_cec.h b/drivers/media/platform/s5p-cec/exynos_hdmi_cec.h new file mode 100644 index 00000000000000..d008695478e2f7 --- /dev/null +++ b/drivers/media/platform/s5p-cec/exynos_hdmi_cec.h @@ -0,0 +1,37 @@ +/* drivers/media/platform/s5p-cec/exynos_hdmi_cec.h + * + * Copyright (c) 2010, 2014 Samsung Electronics + * http://www.samsung.com/ + * + * Header file for interface of Samsung Exynos hdmi cec hardware + * + * 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. + */ + +#ifndef _EXYNOS_HDMI_CEC_H_ +#define _EXYNOS_HDMI_CEC_H_ __FILE__ + +#include +#include +#include "s5p_cec.h" + +void s5p_cec_set_divider(struct s5p_cec_dev *cec); +void s5p_cec_enable_rx(struct s5p_cec_dev *cec); +void s5p_cec_mask_rx_interrupts(struct s5p_cec_dev *cec); +void s5p_cec_unmask_rx_interrupts(struct s5p_cec_dev *cec); +void s5p_cec_mask_tx_interrupts(struct s5p_cec_dev *cec); +void s5p_cec_unmask_tx_interrupts(struct s5p_cec_dev *cec); +void s5p_cec_reset(struct s5p_cec_dev *cec); +void s5p_cec_tx_reset(struct s5p_cec_dev *cec); +void s5p_cec_rx_reset(struct s5p_cec_dev *cec); +void s5p_cec_threshold(struct s5p_cec_dev *cec); +void s5p_cec_copy_packet(struct s5p_cec_dev *cec, char *data, size_t count); +void s5p_cec_set_addr(struct s5p_cec_dev *cec, u32 addr); +u32 s5p_cec_get_status(struct s5p_cec_dev *cec); +void s5p_clr_pending_tx(struct s5p_cec_dev *cec); +void s5p_clr_pending_rx(struct s5p_cec_dev *cec); +void s5p_cec_get_rx_buf(struct s5p_cec_dev *cec, u32 size, u8 *buffer); + +#endif /* _EXYNOS_HDMI_CEC_H_ */ diff --git a/drivers/media/platform/s5p-cec/exynos_hdmi_cecctrl.c b/drivers/media/platform/s5p-cec/exynos_hdmi_cecctrl.c new file mode 100644 index 00000000000000..65fe55e255423e --- /dev/null +++ b/drivers/media/platform/s5p-cec/exynos_hdmi_cecctrl.c @@ -0,0 +1,208 @@ +/* drivers/media/platform/s5p-cec/exynos_hdmi_cecctrl.c + * + * Copyright (c) 2009, 2014 Samsung Electronics + * http://www.samsung.com/ + * + * cec ftn file for Samsung TVOUT driver + * + * 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. + */ + +#include +#include + +#include "exynos_hdmi_cec.h" +#include "regs-cec.h" + +#define S5P_HDMI_FIN 24000000 +#define CEC_DIV_RATIO 320000 + +#define CEC_MESSAGE_BROADCAST_MASK 0x0F +#define CEC_MESSAGE_BROADCAST 0x0F +#define CEC_FILTER_THRESHOLD 0x15 + +void s5p_cec_set_divider(struct s5p_cec_dev *cec) +{ + u32 div_ratio, div_val; + unsigned int reg; + + div_ratio = S5P_HDMI_FIN / CEC_DIV_RATIO - 1; + + if (regmap_read(cec->pmu, EXYNOS_HDMI_PHY_CONTROL, ®)) { + dev_err(cec->dev, "failed to read phy control\n"); + return; + } + + reg = (reg & ~(0x3FF << 16)) | (div_ratio << 16); + + if (regmap_write(cec->pmu, EXYNOS_HDMI_PHY_CONTROL, reg)) { + dev_err(cec->dev, "failed to write phy control\n"); + return; + } + + div_val = CEC_DIV_RATIO * 0.00005 - 1; + + writeb(0x0, cec->reg + S5P_CEC_DIVISOR_3); + writeb(0x0, cec->reg + S5P_CEC_DIVISOR_2); + writeb(0x0, cec->reg + S5P_CEC_DIVISOR_1); + writeb(div_val, cec->reg + S5P_CEC_DIVISOR_0); +} + +void s5p_cec_enable_rx(struct s5p_cec_dev *cec) +{ + u8 reg; + + reg = readb(cec->reg + S5P_CEC_RX_CTRL); + reg |= S5P_CEC_RX_CTRL_ENABLE; + writeb(reg, cec->reg + S5P_CEC_RX_CTRL); +} + +void s5p_cec_mask_rx_interrupts(struct s5p_cec_dev *cec) +{ + u8 reg; + + reg = readb(cec->reg + S5P_CEC_IRQ_MASK); + reg |= S5P_CEC_IRQ_RX_DONE; + reg |= S5P_CEC_IRQ_RX_ERROR; + writeb(reg, cec->reg + S5P_CEC_IRQ_MASK); +} + +void s5p_cec_unmask_rx_interrupts(struct s5p_cec_dev *cec) +{ + u8 reg; + + reg = readb(cec->reg + S5P_CEC_IRQ_MASK); + reg &= ~S5P_CEC_IRQ_RX_DONE; + reg &= ~S5P_CEC_IRQ_RX_ERROR; + writeb(reg, cec->reg + S5P_CEC_IRQ_MASK); +} + +void s5p_cec_mask_tx_interrupts(struct s5p_cec_dev *cec) +{ + u8 reg; + + reg = readb(cec->reg + S5P_CEC_IRQ_MASK); + reg |= S5P_CEC_IRQ_TX_DONE; + reg |= S5P_CEC_IRQ_TX_ERROR; + writeb(reg, cec->reg + S5P_CEC_IRQ_MASK); + +} + +void s5p_cec_unmask_tx_interrupts(struct s5p_cec_dev *cec) +{ + u8 reg; + + reg = readb(cec->reg + S5P_CEC_IRQ_MASK); + reg &= ~S5P_CEC_IRQ_TX_DONE; + reg &= ~S5P_CEC_IRQ_TX_ERROR; + writeb(reg, cec->reg + S5P_CEC_IRQ_MASK); +} + +void s5p_cec_reset(struct s5p_cec_dev *cec) +{ + u8 reg; + + writeb(S5P_CEC_RX_CTRL_RESET, cec->reg + S5P_CEC_RX_CTRL); + writeb(S5P_CEC_TX_CTRL_RESET, cec->reg + S5P_CEC_TX_CTRL); + + reg = readb(cec->reg + 0xc4); + reg &= ~0x1; + writeb(reg, cec->reg + 0xc4); +} + +void s5p_cec_tx_reset(struct s5p_cec_dev *cec) +{ + writeb(S5P_CEC_TX_CTRL_RESET, cec->reg + S5P_CEC_TX_CTRL); +} + +void s5p_cec_rx_reset(struct s5p_cec_dev *cec) +{ + u8 reg; + + writeb(S5P_CEC_RX_CTRL_RESET, cec->reg + S5P_CEC_RX_CTRL); + + reg = readb(cec->reg + 0xc4); + reg &= ~0x1; + writeb(reg, cec->reg + 0xc4); +} + +void s5p_cec_threshold(struct s5p_cec_dev *cec) +{ + writeb(CEC_FILTER_THRESHOLD, cec->reg + S5P_CEC_RX_FILTER_TH); + writeb(0, cec->reg + S5P_CEC_RX_FILTER_CTRL); +} + +void s5p_cec_copy_packet(struct s5p_cec_dev *cec, char *data, size_t count) +{ + char debug[40]; + int i = 0; + u8 reg; + + while (i < count) { + writeb(data[i], cec->reg + (S5P_CEC_TX_BUFF0 + (i * 4))); + sprintf(debug + i * 2, "%02x ", data[i]); + i++; + } + + writeb(count, cec->reg + S5P_CEC_TX_BYTES); + reg = readb(cec->reg + S5P_CEC_TX_CTRL); + reg |= S5P_CEC_TX_CTRL_START; + + if ((data[0] & CEC_MESSAGE_BROADCAST_MASK) == CEC_MESSAGE_BROADCAST) { + dev_err(cec->dev, "Broadcast"); + reg |= S5P_CEC_TX_CTRL_BCAST; + } else { + dev_err(cec->dev, "No Broadcast"); + reg &= ~S5P_CEC_TX_CTRL_BCAST; + } + + reg |= 0x50; + writeb(reg, cec->reg + S5P_CEC_TX_CTRL); + dev_err(cec->dev, "cec-tx: cec count(%d): %s", count, debug); +} + +void s5p_cec_set_addr(struct s5p_cec_dev *cec, u32 addr) +{ + writeb(addr & 0x0F, cec->reg + S5P_CEC_LOGIC_ADDR); +} + +u32 s5p_cec_get_status(struct s5p_cec_dev *cec) +{ + u32 status = 0; + + status = readb(cec->reg + S5P_CEC_STATUS_0); + status |= readb(cec->reg + S5P_CEC_STATUS_1) << 8; + status |= readb(cec->reg + S5P_CEC_STATUS_2) << 16; + status |= readb(cec->reg + S5P_CEC_STATUS_3) << 24; + + dev_dbg(cec->dev, "status = 0x%x!\n", status); + + return status; +} + +void s5p_clr_pending_tx(struct s5p_cec_dev *cec) +{ + writeb(S5P_CEC_IRQ_TX_DONE | S5P_CEC_IRQ_TX_ERROR, + cec->reg + S5P_CEC_IRQ_CLEAR); +} + +void s5p_clr_pending_rx(struct s5p_cec_dev *cec) +{ + writeb(S5P_CEC_IRQ_RX_DONE | S5P_CEC_IRQ_RX_ERROR, + cec->reg + S5P_CEC_IRQ_CLEAR); +} + +void s5p_cec_get_rx_buf(struct s5p_cec_dev *cec, u32 size, u8 *buffer) +{ + u32 i = 0; + char debug[40]; + + while (i < size) { + buffer[i] = readb(cec->reg + S5P_CEC_RX_BUFF0 + (i * 4)); + sprintf(debug + i * 2, "%02x ", buffer[i]); + i++; + } + dev_err(cec->dev, "cec-rx: cec size(%d): %s", size, debug); +} diff --git a/drivers/media/platform/s5p-cec/regs-cec.h b/drivers/media/platform/s5p-cec/regs-cec.h new file mode 100644 index 00000000000000..b2e7e129920e50 --- /dev/null +++ b/drivers/media/platform/s5p-cec/regs-cec.h @@ -0,0 +1,96 @@ +/* drivers/media/platform/s5p-cec/regs-cec.h + * + * Copyright (c) 2010 Samsung Electronics + * http://www.samsung.com/ + * + * register header file for Samsung TVOUT driver + * + * 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. + */ + +#ifndef __EXYNOS_REGS__H +#define __EXYNOS_REGS__H + +/* + * Register part + */ +#define S5P_CEC_STATUS_0 (0x0000) +#define S5P_CEC_STATUS_1 (0x0004) +#define S5P_CEC_STATUS_2 (0x0008) +#define S5P_CEC_STATUS_3 (0x000C) +#define S5P_CEC_IRQ_MASK (0x0010) +#define S5P_CEC_IRQ_CLEAR (0x0014) +#define S5P_CEC_LOGIC_ADDR (0x0020) +#define S5P_CEC_DIVISOR_0 (0x0030) +#define S5P_CEC_DIVISOR_1 (0x0034) +#define S5P_CEC_DIVISOR_2 (0x0038) +#define S5P_CEC_DIVISOR_3 (0x003C) + +#define S5P_CEC_TX_CTRL (0x0040) +#define S5P_CEC_TX_BYTES (0x0044) +#define S5P_CEC_TX_STAT0 (0x0060) +#define S5P_CEC_TX_STAT1 (0x0064) +#define S5P_CEC_TX_BUFF0 (0x0080) +#define S5P_CEC_TX_BUFF1 (0x0084) +#define S5P_CEC_TX_BUFF2 (0x0088) +#define S5P_CEC_TX_BUFF3 (0x008C) +#define S5P_CEC_TX_BUFF4 (0x0090) +#define S5P_CEC_TX_BUFF5 (0x0094) +#define S5P_CEC_TX_BUFF6 (0x0098) +#define S5P_CEC_TX_BUFF7 (0x009C) +#define S5P_CEC_TX_BUFF8 (0x00A0) +#define S5P_CEC_TX_BUFF9 (0x00A4) +#define S5P_CEC_TX_BUFF10 (0x00A8) +#define S5P_CEC_TX_BUFF11 (0x00AC) +#define S5P_CEC_TX_BUFF12 (0x00B0) +#define S5P_CEC_TX_BUFF13 (0x00B4) +#define S5P_CEC_TX_BUFF14 (0x00B8) +#define S5P_CEC_TX_BUFF15 (0x00BC) + +#define S5P_CEC_RX_CTRL (0x00C0) +#define S5P_CEC_RX_STAT0 (0x00E0) +#define S5P_CEC_RX_STAT1 (0x00E4) +#define S5P_CEC_RX_BUFF0 (0x0100) +#define S5P_CEC_RX_BUFF1 (0x0104) +#define S5P_CEC_RX_BUFF2 (0x0108) +#define S5P_CEC_RX_BUFF3 (0x010C) +#define S5P_CEC_RX_BUFF4 (0x0110) +#define S5P_CEC_RX_BUFF5 (0x0114) +#define S5P_CEC_RX_BUFF6 (0x0118) +#define S5P_CEC_RX_BUFF7 (0x011C) +#define S5P_CEC_RX_BUFF8 (0x0120) +#define S5P_CEC_RX_BUFF9 (0x0124) +#define S5P_CEC_RX_BUFF10 (0x0128) +#define S5P_CEC_RX_BUFF11 (0x012C) +#define S5P_CEC_RX_BUFF12 (0x0130) +#define S5P_CEC_RX_BUFF13 (0x0134) +#define S5P_CEC_RX_BUFF14 (0x0138) +#define S5P_CEC_RX_BUFF15 (0x013C) + +#define S5P_CEC_RX_FILTER_CTRL (0x0180) +#define S5P_CEC_RX_FILTER_TH (0x0184) + +/* + * Bit definition part + */ +#define S5P_CEC_IRQ_TX_DONE (1<<0) +#define S5P_CEC_IRQ_TX_ERROR (1<<1) +#define S5P_CEC_IRQ_RX_DONE (1<<4) +#define S5P_CEC_IRQ_RX_ERROR (1<<5) + +#define S5P_CEC_TX_CTRL_START (1<<0) +#define S5P_CEC_TX_CTRL_BCAST (1<<1) +#define S5P_CEC_TX_CTRL_RETRY (0x04<<4) +#define S5P_CEC_TX_CTRL_RESET (1<<7) + +#define S5P_CEC_RX_CTRL_ENABLE (1<<0) +#define S5P_CEC_RX_CTRL_RESET (1<<7) + +#define S5P_CEC_LOGIC_ADDR_MASK (0xF) + +/* PMU Registers for PHY */ +#define EXYNOS_HDMI_PHY_CONTROL 0x700 + +#endif /* __EXYNOS_REGS__H */ diff --git a/drivers/media/platform/s5p-cec/s5p_cec.c b/drivers/media/platform/s5p-cec/s5p_cec.c new file mode 100644 index 00000000000000..e8869a6e293835 --- /dev/null +++ b/drivers/media/platform/s5p-cec/s5p_cec.c @@ -0,0 +1,290 @@ +/* drivers/media/platform/s5p-cec/s5p_cec.c + * + * Samsung S5P CEC driver + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This driver is based on the "cec interface driver for exynos soc" by + * SangPil Moon. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "exynos_hdmi_cec.h" +#include "regs-cec.h" +#include "s5p_cec.h" + +#define CEC_NAME "s5p-cec" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "debug level (0-2)"); + +static int s5p_cec_enable(struct cec_adapter *adap, bool enable) +{ + struct s5p_cec_dev *cec = container_of(adap, struct s5p_cec_dev, adap); + int ret; + + printk(KERN_ERR "%s:%s:%d enable=%s\n", __FILE__, __func__, __LINE__, (enable?"true":"false")); + + if (enable) { + /* TODO get physical address from edid */ + ret = pm_runtime_get_sync(cec->dev); + + adap->phys_addr = 0x100b; + s5p_cec_reset(cec); + + s5p_cec_set_divider(cec); + s5p_cec_threshold(cec); + + s5p_cec_unmask_tx_interrupts(cec); + s5p_cec_unmask_rx_interrupts(cec); + s5p_cec_enable_rx(cec); + } else { + s5p_cec_mask_tx_interrupts(cec); + s5p_cec_mask_rx_interrupts(cec); + pm_runtime_disable(cec->dev); + } + + return 0; +} + +static int s5p_cec_log_addr(struct cec_adapter *adap, u8 addr) +{ + struct s5p_cec_dev *cec = container_of(adap, struct s5p_cec_dev, adap); + + s5p_cec_set_addr(cec, addr); + return 0; +} + +static int s5p_cec_trasmit(struct cec_adapter *adap, struct cec_msg *msg) +{ + struct s5p_cec_dev *cec = container_of(adap, struct s5p_cec_dev, adap); + + s5p_cec_copy_packet(cec, msg->msg, msg->len); + return 0; +} + +static void s5p_cec_transmit_timed_out(struct cec_adapter *adap) +{ + +} + +static irqreturn_t s5p_cec_irq_handler(int irq, void *priv) +{ + struct s5p_cec_dev *cec = priv; + u32 status = 0; + + status = s5p_cec_get_status(cec); + + dev_dbg(cec->dev,"irq received\n"); + + if (status & CEC_STATUS_TX_DONE) { + if (status & CEC_STATUS_TX_ERROR) { + dev_err(cec->dev, "CEC_STATUS_TX_ERROR set\n"); + cec->tx = STATE_ERROR; + } else { + dev_err(cec->dev, "CEC_STATUS_TX_DONE\n"); + cec->tx = STATE_DONE; + } + s5p_clr_pending_tx(cec); + } + + if (status & CEC_STATUS_RX_DONE) { + if (status & CEC_STATUS_RX_ERROR) { + dev_dbg(cec->dev, "CEC_STATUS_RX_ERROR set\n"); + s5p_cec_rx_reset(cec); + s5p_cec_enable_rx(cec); + } else { + dev_dbg(cec->dev, "CEC_STATUS_RX_DONE set\n"); + if (cec->rx != STATE_IDLE) + dev_dbg(cec->dev, "Buffer overrun (worker did not process previous message)\n"); + cec->rx = STATE_BUSY; + cec->msg.len = status >> 24; + cec->msg.status = CEC_RX_STATUS_READY; + s5p_cec_get_rx_buf(cec, cec->msg.len, + cec->msg.msg); + cec->rx = STATE_DONE; + s5p_cec_enable_rx(cec); + } + /* Clear interrupt pending bit */ + s5p_clr_pending_rx(cec); + } + return IRQ_WAKE_THREAD; +} + +static irqreturn_t s5p_cec_irq_handler_thread(int irq, void *priv) +{ + struct s5p_cec_dev *cec = priv; + + dev_dbg(cec->dev,"irq processing thread\n"); + switch (cec->tx) { + case STATE_DONE: + cec_transmit_done(&cec->adap, CEC_TX_STATUS_OK); + cec->tx = STATE_IDLE; + break; + case STATE_ERROR: + cec_transmit_done(&cec->adap, CEC_TX_STATUS_RETRY_TIMEOUT); + cec->tx = STATE_IDLE; + break; + case STATE_BUSY: + dev_err(cec->dev, "state set to busy, this should not occur here\n"); + break; + default: + break; + } + + switch(cec->rx) { + case STATE_DONE: + cec_received_msg(&cec->adap, &cec->msg); + cec->rx = STATE_IDLE; + default: + break; + }; + + return IRQ_HANDLED; +} + +static int s5p_cec_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct s5p_cec_dev *cec; + int ret; + + cec = devm_kzalloc(&pdev->dev, sizeof(*cec), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + cec->dev = dev; + cec->gpio = of_get_named_gpio(dev->of_node, "cec-gpio", 0); + if (IS_ERR_VALUE(cec->gpio)) + return cec->gpio; + + cec->irq = platform_get_irq(pdev, 0); + if (IS_ERR_VALUE(cec->irq)) + return cec->irq; + + ret = devm_request_threaded_irq(dev, cec->irq, s5p_cec_irq_handler, + s5p_cec_irq_handler_thread, IRQF_DISABLED, pdev->name, cec); + if (IS_ERR_VALUE(ret)) + return ret; + + cec->clk = devm_clk_get(dev, "hdmicec"); + if (IS_ERR(cec->clk)) + return PTR_ERR(cec->clk); + + cec->pmu = syscon_regmap_lookup_by_phandle(dev->of_node, + "samsung,syscon-phandle"); + if (IS_ERR(cec->pmu)) + return -EPROBE_DEFER; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + cec->reg = devm_ioremap_resource(dev, res); + if (IS_ERR(cec->reg)) + return PTR_ERR(cec->reg); + + cec->adap.adap_enable = s5p_cec_enable; + cec->adap.adap_log_addr = s5p_cec_log_addr; + cec->adap.adap_transmit = s5p_cec_trasmit; + cec->adap.adap_transmit_timed_out = s5p_cec_transmit_timed_out; + cec_create_adapter(&cec->adap, CEC_NAME, CEC_CAP_STATE | + CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT | + CEC_CAP_RECEIVE); + + platform_set_drvdata(pdev, cec); + pm_runtime_enable(dev); + + dev_dbg(dev, "successfuly probed\n"); + return 0; +} + +static int s5p_cec_remove(struct platform_device *pdev) +{ + struct s5p_cec_dev *cec = platform_get_drvdata(pdev); + + cec_delete_adapter(&cec->adap); + pm_runtime_disable(&pdev->dev); + return 0; +} + +static int s5p_cec_runtime_suspend(struct device *dev) +{ + struct s5p_cec_dev *cec = dev_get_drvdata(dev); + + clk_disable_unprepare(cec->clk); + return 0; +} + +static int s5p_cec_runtime_resume(struct device *dev) +{ + struct s5p_cec_dev *cec = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(cec->clk); + if (ret < 0) + return ret; + return 0; +} + +static int s5p_cec_suspend(struct device *dev) +{ + if (pm_runtime_suspended(dev)) + return 0; + return s5p_cec_runtime_suspend(dev); +} + +static int s5p_cec_resume(struct device *dev) +{ + if (pm_runtime_suspended(dev)) + return 0; + return s5p_cec_runtime_resume(dev); +} + +static const struct dev_pm_ops s5p_cec_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(s5p_cec_suspend, s5p_cec_resume) + SET_RUNTIME_PM_OPS(s5p_cec_runtime_suspend, s5p_cec_runtime_resume, + NULL) +}; + +static const struct of_device_id s5p_cec_match[] = { + { + .compatible = "samsung,s5p-cec", + }, + {}, +}; + +static struct platform_driver s5p_cec_pdrv = { + .probe = s5p_cec_probe, + .remove = s5p_cec_remove, + .driver = { + .name = CEC_NAME, + .owner = THIS_MODULE, + .of_match_table = s5p_cec_match, + .pm = &s5p_cec_pm_ops, + }, +}; + +module_platform_driver(s5p_cec_pdrv); + +MODULE_AUTHOR("Kamil Debski "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Samsung S5P CEC driver"); + diff --git a/drivers/media/platform/s5p-cec/s5p_cec.h b/drivers/media/platform/s5p-cec/s5p_cec.h new file mode 100644 index 00000000000000..0c082f018253a0 --- /dev/null +++ b/drivers/media/platform/s5p-cec/s5p_cec.h @@ -0,0 +1,113 @@ +/* drivers/media/platform/s5p-cec/s5p_cec.h + * + * Samsung S5P HDMI CEC driver + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _S5P_CEC_H_ +#define _S5P_CEC_H_ __FILE__ +/* +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "regs-cec.h" +*/ +//#include "exynos_hdmi_cec.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "exynos_hdmi_cec.h" +#include "regs-cec.h" +#include "s5p_cec.h" + +#define CEC_NAME "s5p-cec" + +#define CEC_STATUS_TX_RUNNING (1 << 0) +#define CEC_STATUS_TX_TRANSFERRING (1 << 1) +#define CEC_STATUS_TX_DONE (1 << 2) +#define CEC_STATUS_TX_ERROR (1 << 3) +#define CEC_STATUS_TX_BYTES (0xFF << 8) +#define CEC_STATUS_RX_RUNNING (1 << 16) +#define CEC_STATUS_RX_RECEIVING (1 << 17) +#define CEC_STATUS_RX_DONE (1 << 18) +#define CEC_STATUS_RX_ERROR (1 << 19) +#define CEC_STATUS_RX_BCAST (1 << 20) +#define CEC_STATUS_RX_BYTES (0xFF << 24) + +#define CEC_WORKER_TX_DONE (1 << 0) +#define CEC_WORKER_RX_MSG (1 << 1) + +/* CEC Rx buffer size */ +#define CEC_RX_BUFF_SIZE 16 +/* CEC Tx buffer size */ +#define CEC_TX_BUFF_SIZE 16 + +enum cec_state { + STATE_IDLE, + STATE_BUSY, + STATE_DONE, + STATE_ERROR +}; + +struct s5p_cec_dev { + struct cec_adapter adap; + struct clk *clk; + struct device *dev; + struct mutex lock; + struct regmap *pmu; + int gpio; + int irq; + void __iomem *reg; + + enum cec_state rx; + enum cec_state tx; + struct cec_msg msg; +}; + +#endif /* _S5P_CEC_H_ */ From a1a14d90dffdec836b71627c1ec74acd93310ad6 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Fri, 23 Jan 2015 18:49:54 +0100 Subject: [PATCH 685/788] patchset: [RFC v2 0/7] HDMI CEC framework --- PATCHES | 1 + 1 file changed, 1 insertion(+) diff --git a/PATCHES b/PATCHES index a9c25e72c6c70d..05174472429186 100644 --- a/PATCHES +++ b/PATCHES @@ -5,3 +5,4 @@ [PATCH v6 00/18] thermal: exynos: Thermal code rework to use device tree [PATCH v5 00/18] Exynos SYSMMU (IOMMU) integration with DT and DMA-mapping subsystem [PATCH v4 0/2] serial: samsung: add support for early console +[RFC v2 0/7] HDMI CEC framework From 13ea1511c71bf4e36074455221444a11f81ea1d2 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 26 Aug 2014 14:09:43 +0200 Subject: [PATCH 686/788] drivers: of: add support for named memory regions This patch adds a code to initialize memory regions also for child devices if parent device has "memory-region" and "memory-region-names" device tree properties and given device's name matches ":" template. Signed-off-by: Marek Szyprowski --- .../reserved-memory/reserved-memory.txt | 6 +- drivers/of/of_reserved_mem.c | 99 +++++++++++++------ 2 files changed, 73 insertions(+), 32 deletions(-) diff --git a/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt b/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt index 3da0ebdba8d90e..69d28288ed379a 100644 --- a/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt +++ b/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt @@ -73,7 +73,11 @@ Device node references to reserved memory Regions in the /reserved-memory node may be referenced by other device nodes by adding a memory-region property to the device node. -memory-region (optional) - phandle, specifier pairs to children of /reserved-memory +memory-region (optional) - arrays of phandles, specifier pairs to children + of /reserved-memory, first phandle is used as a default memory + region +memory-region-names (optional) - array of strings with names of memory + regions, used when more than one region has been defined Example ------- diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index dc566b38645f6f..822dec40d453ac 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -237,25 +237,10 @@ static inline struct reserved_mem *__find_rmem(struct device_node *node) return NULL; } -/** - * of_reserved_mem_device_init() - assign reserved memory region to given device - * - * This function assign memory region pointed by "memory-region" device tree - * property to the given device. - */ -int of_reserved_mem_device_init(struct device *dev) +static int __rmem_dev_init(struct device *dev, struct device_node *np) { - struct reserved_mem *rmem; - struct device_node *np; int ret; - - np = of_parse_phandle(dev->of_node, "memory-region", 0); - if (!np) - return -ENODEV; - - rmem = __find_rmem(np); - of_node_put(np); - + struct reserved_mem *rmem = __find_rmem(np); if (!rmem || !rmem->ops || !rmem->ops->device_init) return -EINVAL; @@ -266,6 +251,71 @@ int of_reserved_mem_device_init(struct device *dev) return ret; } +static int __rmem_dev_release(struct device *dev, struct device_node *np) +{ + struct reserved_mem *rmem = __find_rmem(np); + if (!rmem || !rmem->ops) + return -EINVAL; + + if (rmem->ops->device_release) + rmem->ops->device_release(rmem, dev); + return 0; +} + +static int __rmem_dev_call(struct device *dev, + int (*func)(struct device *dev, struct device_node *np)) +{ + int ret = -ENODEV; + if (of_get_property(dev->of_node, "memory-region", NULL)) { + struct device_node *np; + np = of_parse_phandle(dev->of_node, "memory-region", 0); + if (!np) + return -ENODEV; + ret = func(dev, np); + of_node_put(np); + } else if (dev->parent && + of_get_property(dev->parent->of_node, "memory-region", + NULL)) { + struct device *parent = dev->parent; + struct device_node *np; + char *name; + int idx; + + name = strrchr(dev_name(dev), ':'); + if (!name) + return -ENODEV; + name++; + + idx = of_property_match_string(parent->of_node, + "memory-region-names", name); + if (idx < 0) + return -ENODEV; + + np = of_parse_phandle(parent->of_node, "memory-region", idx); + if (!np) + return -ENODEV; + + ret = func(dev, np); + of_node_put(np); + } + return ret; +} + +/** + * of_reserved_mem_device_init() - assign reserved memory region to given device + * + * This function assigns default memory region pointed by first entry of + * "memory-region" device tree property (if available) to the given device or + * checks if the given device can be used to give access to named memory region + * if parent device has "memory-region" and "memory-region-names" device tree + * properties and given device's name matches ":" + * template. + */ +int of_reserved_mem_device_init(struct device *dev) +{ + return __rmem_dev_call(dev, __rmem_dev_init); +} + /** * of_reserved_mem_device_release() - release reserved memory device structures * @@ -274,18 +324,5 @@ int of_reserved_mem_device_init(struct device *dev) */ void of_reserved_mem_device_release(struct device *dev) { - struct reserved_mem *rmem; - struct device_node *np; - - np = of_parse_phandle(dev->of_node, "memory-region", 0); - if (!np) - return; - - rmem = __find_rmem(np); - of_node_put(np); - - if (!rmem || !rmem->ops || !rmem->ops->device_release) - return; - - rmem->ops->device_release(rmem, dev); + __rmem_dev_call(dev, __rmem_dev_release); } From 3dc043b8a255b1c5bef99580a55d56402979bd4c Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 26 Aug 2014 14:09:46 +0200 Subject: [PATCH 687/788] media: s5p-mfc: replace custom reserved memory init code with generic one This patch removes custom initialization of reserved memory regions from s5p-mfc driver and adds a new code for handling reserved memory with generic named reserved memory regions read from device tree. s5p-mfc driver now handles two reserved memory regions: "left" and "right", defined by generic reserved memory bindings. Support for non-dt platform has been removed, because all supported platforms have been converted to device tree. Signed-off-by: Marek Szyprowski --- drivers/media/platform/s5p-mfc/s5p_mfc.c | 102 ++++++++--------------- 1 file changed, 35 insertions(+), 67 deletions(-) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index fbfdf03b9054ac..cd722cc52fd0d9 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "s5p_mfc_common.h" #include "s5p_mfc_ctrl.h" @@ -1006,55 +1007,40 @@ static const struct v4l2_file_operations s5p_mfc_fops = { .mmap = s5p_mfc_mmap, }; -static int match_child(struct device *dev, void *data) +static struct device *s5p_mfc_alloc_memdev(struct device *dev, const char *name) { - if (!dev_name(dev)) - return 0; - return !strcmp(dev_name(dev), (char *)data); -} - -static void *mfc_get_drv_data(struct platform_device *pdev); - -static int s5p_mfc_alloc_memdevs(struct s5p_mfc_dev *dev) -{ - unsigned int mem_info[2] = { }; + struct device *child; + int ret; - dev->mem_dev_l = devm_kzalloc(&dev->plat_dev->dev, - sizeof(struct device), GFP_KERNEL); - if (!dev->mem_dev_l) { - mfc_err("Not enough memory\n"); - return -ENOMEM; - } - device_initialize(dev->mem_dev_l); - of_property_read_u32_array(dev->plat_dev->dev.of_node, - "samsung,mfc-l", mem_info, 2); - if (dma_declare_coherent_memory(dev->mem_dev_l, mem_info[0], - mem_info[0], mem_info[1], - DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) { - mfc_err("Failed to declare coherent memory for\n" - "MFC device\n"); - return -ENOMEM; + child = devm_kzalloc(dev, sizeof(struct device), GFP_KERNEL); + if (!child) + return NULL; + + device_initialize(child); + dev_set_name(child, "%s:%s", dev_name(dev), name); + child->parent = dev; + child->bus = dev->bus; + child->coherent_dma_mask = dev->coherent_dma_mask; + child->dma_mask = dev->dma_mask; + + if (device_add(child) == 0) { + ret = of_reserved_mem_device_init(child); + if (ret == 0) + return child; } - dev->mem_dev_r = devm_kzalloc(&dev->plat_dev->dev, - sizeof(struct device), GFP_KERNEL); - if (!dev->mem_dev_r) { - mfc_err("Not enough memory\n"); - return -ENOMEM; - } - device_initialize(dev->mem_dev_r); - of_property_read_u32_array(dev->plat_dev->dev.of_node, - "samsung,mfc-r", mem_info, 2); - if (dma_declare_coherent_memory(dev->mem_dev_r, mem_info[0], - mem_info[0], mem_info[1], - DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) { - pr_err("Failed to declare coherent memory for\n" - "MFC device\n"); - return -ENOMEM; - } - return 0; + put_device(child); + return NULL; } +void s5p_mfc_free_memdev(struct device *dev) +{ + of_reserved_mem_device_release(dev); + put_device(dev); +} + +static void *mfc_get_drv_data(struct platform_device *pdev); + /* MFC probe function */ static int s5p_mfc_probe(struct platform_device *pdev) { @@ -1106,26 +1092,8 @@ static int s5p_mfc_probe(struct platform_device *pdev) goto err_res; } - if (pdev->dev.of_node) { - ret = s5p_mfc_alloc_memdevs(dev); - if (ret < 0) - goto err_res; - } else { - dev->mem_dev_l = device_find_child(&dev->plat_dev->dev, - "s5p-mfc-l", match_child); - if (!dev->mem_dev_l) { - mfc_err("Mem child (L) device get failed\n"); - ret = -ENODEV; - goto err_res; - } - dev->mem_dev_r = device_find_child(&dev->plat_dev->dev, - "s5p-mfc-r", match_child); - if (!dev->mem_dev_r) { - mfc_err("Mem child (R) device get failed\n"); - ret = -ENODEV; - goto err_res; - } - } + dev->mem_dev_l = s5p_mfc_alloc_memdev(&dev->plat_dev->dev, "left"); + dev->mem_dev_r = s5p_mfc_alloc_memdev(&dev->plat_dev->dev, "right"); dev->alloc_ctx[0] = vb2_dma_contig_init_ctx(dev->mem_dev_l); if (IS_ERR(dev->alloc_ctx[0])) { @@ -1256,10 +1224,10 @@ static int s5p_mfc_remove(struct platform_device *pdev) s5p_mfc_release_firmware(dev); vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]); vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]); - if (pdev->dev.of_node) { - put_device(dev->mem_dev_l); - put_device(dev->mem_dev_r); - } + if (dev->mem_dev_l) + s5p_mfc_free_memdev(dev->mem_dev_l); + if (dev->mem_dev_r) + s5p_mfc_free_memdev(dev->mem_dev_r); s5p_mfc_final_pm(dev); return 0; From f905c4927d90ba986da3fee8f3ae00c2577c1fed Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Fri, 29 Aug 2014 14:40:20 +0200 Subject: [PATCH 688/788] drivers: of: export of_reserved_mem_device_{init,release} The s5p-mfc driver can be build as module and uses these calls during setup. Export them so they can be used. --- drivers/of/of_reserved_mem.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index 822dec40d453ac..69bee8618cd7b9 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -315,6 +315,7 @@ int of_reserved_mem_device_init(struct device *dev) { return __rmem_dev_call(dev, __rmem_dev_init); } +EXPORT_SYMBOL(of_reserved_mem_device_init); /** * of_reserved_mem_device_release() - release reserved memory device structures @@ -326,3 +327,4 @@ void of_reserved_mem_device_release(struct device *dev) { __rmem_dev_call(dev, __rmem_dev_release); } +EXPORT_SYMBOL(of_reserved_mem_device_release); From 567704fb820775b880e11efbbfb18b4ed560433d Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 26 Aug 2014 14:09:47 +0200 Subject: [PATCH 689/788] ARM: Exynos: convert MFC to generic reserved memory bindings Support for reserved memory for MFC device (samsung,mfc-r and samsung,mfc-l properties) was merged quickly without any deep review and discussion. This patch replaces those custom properties with support for generic reserved memory bindings. All custom code for handling MFC-specific reserved memory can be now removed. Signed-off-by: Marek Szyprowski --- .../devicetree/bindings/media/s5p-mfc.txt | 16 ++-- arch/arm/boot/dts/exynos4210-origen.dts | 22 ++++- arch/arm/boot/dts/exynos4210-smdkv310.dts | 22 ++++- arch/arm/boot/dts/exynos4412-origen.dts | 22 ++++- arch/arm/boot/dts/exynos4412-smdk4412.dts | 22 ++++- arch/arm/boot/dts/exynos5250-arndale.dts | 22 ++++- arch/arm/boot/dts/exynos5250-smdk5250.dts | 22 ++++- arch/arm/boot/dts/exynos5420-arndale-octa.dts | 22 ++++- arch/arm/boot/dts/exynos5420-smdk5420.dts | 22 ++++- arch/arm/mach-exynos/exynos.c | 18 ---- arch/arm/mach-exynos/mfc.h | 16 ---- arch/arm/plat-samsung/Kconfig | 5 - arch/arm/plat-samsung/Makefile | 1 - arch/arm/plat-samsung/s5p-dev-mfc.c | 94 ------------------- 14 files changed, 168 insertions(+), 158 deletions(-) delete mode 100644 arch/arm/mach-exynos/mfc.h delete mode 100644 arch/arm/plat-samsung/s5p-dev-mfc.c diff --git a/Documentation/devicetree/bindings/media/s5p-mfc.txt b/Documentation/devicetree/bindings/media/s5p-mfc.txt index 2d5787eac91af9..4603673c593ba0 100644 --- a/Documentation/devicetree/bindings/media/s5p-mfc.txt +++ b/Documentation/devicetree/bindings/media/s5p-mfc.txt @@ -21,16 +21,16 @@ Required properties: - clock-names : from common clock binding: must contain "mfc", corresponding to entry in the clocks property. - - samsung,mfc-r : Base address of the first memory bank used by MFC - for DMA contiguous memory allocation and its size. - - - samsung,mfc-l : Base address of the second memory bank used by MFC - for DMA contiguous memory allocation and its size. - Optional properties: - power-domains : power-domain property defined with a phandle to respective power domain. + - memory-region : from reserved memory binding: phandles to two reserved + memory regions: accessed by "left" and "right" mfc memory bus + interfaces, used when no SYSMMU support is available + - memory-region-names : from reserved memory binding: must be "left" + and "right" + Example: SoC specific DT entry: @@ -46,6 +46,6 @@ mfc: codec@13400000 { Board specific DT entry: codec@13400000 { - samsung,mfc-r = <0x43000000 0x800000>; - samsung,mfc-l = <0x51000000 0x800000>; + memory-region = <&mfc_left>, <&mfc_right>; + memory-region-names = "left", "right"; }; diff --git a/arch/arm/boot/dts/exynos4210-origen.dts b/arch/arm/boot/dts/exynos4210-origen.dts index b811461414023f..a519c605177672 100644 --- a/arch/arm/boot/dts/exynos4210-origen.dts +++ b/arch/arm/boot/dts/exynos4210-origen.dts @@ -29,6 +29,24 @@ 0x70000000 0x10000000>; }; + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + mfc_left: region@51000000 { + compatible = "shared-dma-pool"; + no-map; + reg = <0x51000000 0x800000>; + }; + + mfc_right: region@43000000 { + compatible = "shared-dma-pool"; + no-map; + reg = <0x43000000 0x800000>; + }; + }; + chosen { bootargs ="root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC2,115200 init=/linuxrc"; stdout-path = &serial_2; @@ -83,8 +101,8 @@ }; codec@13400000 { - samsung,mfc-r = <0x43000000 0x800000>; - samsung,mfc-l = <0x51000000 0x800000>; + memory-region = <&mfc_left>, <&mfc_right>; + memory-region-names = "left", "right"; status = "okay"; }; diff --git a/arch/arm/boot/dts/exynos4210-smdkv310.dts b/arch/arm/boot/dts/exynos4210-smdkv310.dts index 86216fff1b4f42..e7748802aae3ba 100644 --- a/arch/arm/boot/dts/exynos4210-smdkv310.dts +++ b/arch/arm/boot/dts/exynos4210-smdkv310.dts @@ -25,6 +25,24 @@ reg = <0x40000000 0x80000000>; }; + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + mfc_left: region@51000000 { + compatible = "shared-dma-pool"; + no-map; + reg = <0x51000000 0x800000>; + }; + + mfc_right: region@43000000 { + compatible = "shared-dma-pool"; + no-map; + reg = <0x43000000 0x800000>; + }; + }; + chosen { bootargs = "root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC1,115200 init=/linuxrc"; stdout-path = &serial_1; @@ -42,8 +60,8 @@ }; codec@13400000 { - samsung,mfc-r = <0x43000000 0x800000>; - samsung,mfc-l = <0x51000000 0x800000>; + memory-region = <&mfc_left>, <&mfc_right>; + memory-region-names = "left", "right"; status = "okay"; }; diff --git a/arch/arm/boot/dts/exynos4412-origen.dts b/arch/arm/boot/dts/exynos4412-origen.dts index bd8b73077d41fa..34f705e4bd1ac2 100644 --- a/arch/arm/boot/dts/exynos4412-origen.dts +++ b/arch/arm/boot/dts/exynos4412-origen.dts @@ -24,6 +24,24 @@ reg = <0x40000000 0x40000000>; }; + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + mfc_left: region@51000000 { + compatible = "shared-dma-pool"; + no-map; + reg = <0x51000000 0x800000>; + }; + + mfc_right: region@43000000 { + compatible = "shared-dma-pool"; + no-map; + reg = <0x43000000 0x800000>; + }; + }; + chosen { bootargs ="console=ttySAC2,115200"; stdout-path = &serial_2; @@ -148,8 +166,8 @@ }; codec@13400000 { - samsung,mfc-r = <0x43000000 0x800000>; - samsung,mfc-l = <0x51000000 0x800000>; + memory-region = <&mfc_left>, <&mfc_right>; + memory-region-names = "left", "right"; status = "okay"; }; diff --git a/arch/arm/boot/dts/exynos4412-smdk4412.dts b/arch/arm/boot/dts/exynos4412-smdk4412.dts index b9256afbcc683e..e05c49eb3c5966 100644 --- a/arch/arm/boot/dts/exynos4412-smdk4412.dts +++ b/arch/arm/boot/dts/exynos4412-smdk4412.dts @@ -23,6 +23,24 @@ reg = <0x40000000 0x40000000>; }; + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + mfc_left: region@51000000 { + compatible = "shared-dma-pool"; + no-map; + reg = <0x51000000 0x800000>; + }; + + mfc_right: region@43000000 { + compatible = "shared-dma-pool"; + no-map; + reg = <0x43000000 0x800000>; + }; + }; + chosen { bootargs ="root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC1,115200 init=/linuxrc"; stdout-path = &serial_1; @@ -127,8 +145,8 @@ }; codec@13400000 { - samsung,mfc-r = <0x43000000 0x800000>; - samsung,mfc-l = <0x51000000 0x800000>; + memory-region = <&mfc_left>, <&mfc_right>; + memory-region-names = "left", "right"; status = "okay"; }; diff --git a/arch/arm/boot/dts/exynos5250-arndale.dts b/arch/arm/boot/dts/exynos5250-arndale.dts index 7e728a1b55590a..84ac974ec356fc 100644 --- a/arch/arm/boot/dts/exynos5250-arndale.dts +++ b/arch/arm/boot/dts/exynos5250-arndale.dts @@ -23,6 +23,24 @@ reg = <0x40000000 0x80000000>; }; + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + mfc_left: region@51000000 { + compatible = "shared-dma-pool"; + no-map; + reg = <0x51000000 0x800000>; + }; + + mfc_right: region@43000000 { + compatible = "shared-dma-pool"; + no-map; + reg = <0x43000000 0x800000>; + }; + }; + chosen { bootargs = "console=ttySAC2,115200"; }; @@ -514,8 +532,8 @@ }; &mfc { - samsung,mfc-r = <0x43000000 0x800000>; - samsung,mfc-l = <0x51000000 0x800000>; + memory-region = <&mfc_left>, <&mfc_right>; + memory-region-names = "left", "right"; }; &mmc_0 { diff --git a/arch/arm/boot/dts/exynos5250-smdk5250.dts b/arch/arm/boot/dts/exynos5250-smdk5250.dts index bc27cc2558fe65..26cc4c0b8aa321 100644 --- a/arch/arm/boot/dts/exynos5250-smdk5250.dts +++ b/arch/arm/boot/dts/exynos5250-smdk5250.dts @@ -25,6 +25,24 @@ reg = <0x40000000 0x80000000>; }; + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + mfc_left: region@51000000 { + compatible = "shared-dma-pool"; + no-map; + reg = <0x51000000 0x800000>; + }; + + mfc_right: region@43000000 { + compatible = "shared-dma-pool"; + no-map; + reg = <0x43000000 0x800000>; + }; + }; + chosen { bootargs = "root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC2,115200 init=/linuxrc"; }; @@ -338,8 +356,8 @@ }; &mfc { - samsung,mfc-r = <0x43000000 0x800000>; - samsung,mfc-l = <0x51000000 0x800000>; + memory-region = <&mfc_left>, <&mfc_right>; + memory-region-names = "left", "right"; }; &mmc_0 { diff --git a/arch/arm/boot/dts/exynos5420-arndale-octa.dts b/arch/arm/boot/dts/exynos5420-arndale-octa.dts index db2c1c4cd90076..b9d99a2eeae219 100644 --- a/arch/arm/boot/dts/exynos5420-arndale-octa.dts +++ b/arch/arm/boot/dts/exynos5420-arndale-octa.dts @@ -22,6 +22,24 @@ reg = <0x20000000 0x80000000>; }; + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + mfc_left: region@51000000 { + compatible = "shared-dma-pool"; + no-map; + reg = <0x51000000 0x800000>; + }; + + mfc_right: region@43000000 { + compatible = "shared-dma-pool"; + no-map; + reg = <0x43000000 0x800000>; + }; + }; + chosen { bootargs = "console=ttySAC3,115200"; }; @@ -43,8 +61,8 @@ }; codec@11000000 { - samsung,mfc-r = <0x43000000 0x800000>; - samsung,mfc-l = <0x51000000 0x800000>; + memory-region = <&mfc_left>, <&mfc_right>; + memory-region-names = "left", "right"; }; mmc@12200000 { diff --git a/arch/arm/boot/dts/exynos5420-smdk5420.dts b/arch/arm/boot/dts/exynos5420-smdk5420.dts index 8be3d7b489ff35..66912a5ecfc5dc 100644 --- a/arch/arm/boot/dts/exynos5420-smdk5420.dts +++ b/arch/arm/boot/dts/exynos5420-smdk5420.dts @@ -20,6 +20,24 @@ reg = <0x20000000 0x80000000>; }; + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + mfc_left: region@51000000 { + compatible = "shared-dma-pool"; + no-map; + reg = <0x51000000 0x800000>; + }; + + mfc_right: region@43000000 { + compatible = "shared-dma-pool"; + no-map; + reg = <0x43000000 0x800000>; + }; + }; + chosen { bootargs = "console=ttySAC2,115200 init=/linuxrc"; }; @@ -69,8 +87,8 @@ }; codec@11000000 { - samsung,mfc-r = <0x43000000 0x800000>; - samsung,mfc-l = <0x51000000 0x800000>; + memory-region = <&mfc_left>, <&mfc_right>; + memory-region-names = "left", "right"; }; mmc@12200000 { diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c index c78d631d78a7ed..dce79f614c6f48 100644 --- a/arch/arm/mach-exynos/exynos.c +++ b/arch/arm/mach-exynos/exynos.c @@ -28,7 +28,6 @@ #include #include "common.h" -#include "mfc.h" #include "regs-pmu.h" #include "regs-sys.h" @@ -301,22 +300,6 @@ static char const *exynos_dt_compat[] __initconst = { NULL }; -static void __init exynos_reserve(void) -{ -#ifdef CONFIG_S5P_DEV_MFC - int i; - char *mfc_mem[] = { - "samsung,mfc-v5", - "samsung,mfc-v6", - "samsung,mfc-v7", - }; - - for (i = 0; i < ARRAY_SIZE(mfc_mem); i++) - if (of_scan_flat_dt(s5p_fdt_alloc_mfc_mem, mfc_mem[i])) - break; -#endif -} - static void __init exynos_dt_fixup(void) { /* @@ -338,6 +321,5 @@ DT_MACHINE_START(EXYNOS_DT, "SAMSUNG EXYNOS (Flattened Device Tree)") .init_machine = exynos_dt_machine_init, .init_late = exynos_init_late, .dt_compat = exynos_dt_compat, - .reserve = exynos_reserve, .dt_fixup = exynos_dt_fixup, MACHINE_END diff --git a/arch/arm/mach-exynos/mfc.h b/arch/arm/mach-exynos/mfc.h deleted file mode 100644 index dec93cd5b3c6af..00000000000000 --- a/arch/arm/mach-exynos/mfc.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (C) 2013 Samsung Electronics Co.Ltd - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#ifndef __MACH_EXYNOS_MFC_H -#define __MACH_EXYNOS_MFC_H __FILE__ - -int __init s5p_fdt_alloc_mfc_mem(unsigned long node, const char *uname, - int depth, void *data); - -#endif /* __MACH_EXYNOS_MFC_H */ diff --git a/arch/arm/plat-samsung/Kconfig b/arch/arm/plat-samsung/Kconfig index 9bd2776e7d05fb..6b127c28912a58 100644 --- a/arch/arm/plat-samsung/Kconfig +++ b/arch/arm/plat-samsung/Kconfig @@ -260,11 +260,6 @@ config SAMSUNG_DMADEV endif -config S5P_DEV_MFC - bool - help - Compile in setup memory (init) code for MFC - comment "Power management" config SAMSUNG_PM_DEBUG diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-samsung/Makefile index 87746c37f0309d..61b5f65361e4e5 100644 --- a/arch/arm/plat-samsung/Makefile +++ b/arch/arm/plat-samsung/Makefile @@ -20,7 +20,6 @@ obj-$(CONFIG_SAMSUNG_ATAGS) += platformdata.o obj-$(CONFIG_SAMSUNG_ATAGS) += devs.o obj-$(CONFIG_SAMSUNG_ATAGS) += dev-uart.o -obj-$(CONFIG_S5P_DEV_MFC) += s5p-dev-mfc.o obj-$(CONFIG_SAMSUNG_DEV_BACKLIGHT) += dev-backlight.o diff --git a/arch/arm/plat-samsung/s5p-dev-mfc.c b/arch/arm/plat-samsung/s5p-dev-mfc.c deleted file mode 100644 index 0b04b6b0fa302f..00000000000000 --- a/arch/arm/plat-samsung/s5p-dev-mfc.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2010-2011 Samsung Electronics Co.Ltd - * - * Base S5P MFC resource and device definitions - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -static struct platform_device s5p_device_mfc_l; -static struct platform_device s5p_device_mfc_r; - -struct s5p_mfc_dt_meminfo { - unsigned long loff; - unsigned long lsize; - unsigned long roff; - unsigned long rsize; - char *compatible; -}; - -struct s5p_mfc_reserved_mem { - phys_addr_t base; - unsigned long size; - struct device *dev; -}; - -static struct s5p_mfc_reserved_mem s5p_mfc_mem[2] __initdata; - - -static void __init s5p_mfc_reserve_mem(phys_addr_t rbase, unsigned int rsize, - phys_addr_t lbase, unsigned int lsize) -{ - int i; - - s5p_mfc_mem[0].dev = &s5p_device_mfc_r.dev; - s5p_mfc_mem[0].base = rbase; - s5p_mfc_mem[0].size = rsize; - - s5p_mfc_mem[1].dev = &s5p_device_mfc_l.dev; - s5p_mfc_mem[1].base = lbase; - s5p_mfc_mem[1].size = lsize; - - for (i = 0; i < ARRAY_SIZE(s5p_mfc_mem); i++) { - struct s5p_mfc_reserved_mem *area = &s5p_mfc_mem[i]; - if (memblock_remove(area->base, area->size)) { - printk(KERN_ERR "Failed to reserve memory for MFC device (%ld bytes at 0x%08lx)\n", - area->size, (unsigned long) area->base); - area->base = 0; - } - } -} - -int __init s5p_fdt_alloc_mfc_mem(unsigned long node, const char *uname, - int depth, void *data) -{ - const __be32 *prop; - int len; - struct s5p_mfc_dt_meminfo mfc_mem; - - if (!data) - return 0; - - if (!of_flat_dt_is_compatible(node, data)) - return 0; - - prop = of_get_flat_dt_prop(node, "samsung,mfc-l", &len); - if (!prop || (len != 2 * sizeof(unsigned long))) - return 0; - - mfc_mem.loff = be32_to_cpu(prop[0]); - mfc_mem.lsize = be32_to_cpu(prop[1]); - - prop = of_get_flat_dt_prop(node, "samsung,mfc-r", &len); - if (!prop || (len != 2 * sizeof(unsigned long))) - return 0; - - mfc_mem.roff = be32_to_cpu(prop[0]); - mfc_mem.rsize = be32_to_cpu(prop[1]); - - s5p_mfc_reserve_mem(mfc_mem.roff, mfc_mem.rsize, - mfc_mem.loff, mfc_mem.lsize); - - return 1; -} From a1a8ab80f729f85bcad9688514bc6676ab044ce4 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 26 Aug 2014 14:09:48 +0200 Subject: [PATCH 690/788] ARM: DTS: exynos4412-odroid*: enable MFC device Enable support for Multimedia Codec (MFC) device for all Exynos4412-based Odroid boards. Signed-off-by: Marek Szyprowski --- .../boot/dts/exynos4412-odroid-common.dtsi | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index e038ffda6db47e..e24aee9c0bc993 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -16,6 +16,24 @@ stdout-path = &serial_1; }; + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + mfc_left: region@77000000 { + compatible = "shared-dma-pool"; + reusable; + reg = <0x77000000 0x1000000>; + }; + + mfc_right: region@78000000 { + compatible = "shared-dma-pool"; + reusable; + reg = <0x78000000 0x1000000>; + }; + }; + firmware@0204F000 { compatible = "samsung,secure-firmware"; reg = <0x0204F000 0x1000>; @@ -476,6 +494,12 @@ samsung,pin-pud = <0>; samsung,pin-drv = <0>; }; + + codec@13400000 { + status = "okay"; + memory-region = <&mfc_left>, <&mfc_right>; + memory-region-names = "left", "right"; + }; }; &ppmu_dmc0 { From 889140ad1744ca964f3d815f5dc4f05ba264eeb1 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 1 Jul 2014 10:10:08 +0200 Subject: [PATCH 691/788] Exynos: add support for 'domain-always-on' property This patch adds support for domain-always-on property to Exynos power domain driver. Domains with this property as always kept enabled. Signed-off-by: Marek Szyprowski --- .../devicetree/bindings/arm/exynos/power_domain.txt | 1 + arch/arm/mach-exynos/pm_domains.c | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt index 28918a9f81b226..ac66608ad8400d 100644 --- a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt +++ b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt @@ -24,6 +24,7 @@ Optional Properties: are supported currently. - power-domains: generic power domain binding pointing to a master power domain that the given domain is a part of +- domain-always-on: keeps the domain always enabled Node of a device using power domains must have a power-domains property defined with a phandle to respective power domain. diff --git a/arch/arm/mach-exynos/pm_domains.c b/arch/arm/mach-exynos/pm_domains.c index 0bc43815ae52d9..e8597b5f3876ae 100644 --- a/arch/arm/mach-exynos/pm_domains.c +++ b/arch/arm/mach-exynos/pm_domains.c @@ -117,6 +117,7 @@ static __init int exynos4_pm_init_power_domain(void) struct device_node *np; for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") { + struct dev_power_governor *gov = NULL; struct exynos_pm_domain *pd; int on, i; struct device *dev; @@ -137,6 +138,9 @@ static __init int exynos4_pm_init_power_domain(void) return -ENOMEM; } + if (of_property_read_bool(np, "domain-always-on")) + gov = &pm_domain_always_on_gov; + pd->pd.name = kstrdup(np->name, GFP_KERNEL); if (!pd->pd.name) { kfree(pd); @@ -175,7 +179,7 @@ static __init int exynos4_pm_init_power_domain(void) no_clk: on = __raw_readl(pd->base + 0x4) & INT_LOCAL_PWR_EN; - pm_genpd_init(&pd->pd, NULL, !on); + pm_genpd_init(&pd->pd, gov, !on); of_genpd_add_provider_simple(np, &pd->pd); } From df511f48f42e1b2f396a28cc49ceb1cc729893b6 Mon Sep 17 00:00:00 2001 From: Alban Browaeys Date: Fri, 19 Sep 2014 11:32:47 +0200 Subject: [PATCH 692/788] ARM: dts: exynos4412-odroid: fix freeze when reading clk_summary Set the ISP power domain to always-on to avoid a lockup when reading the 'clk_summary' debugfs entry on a Odroid-U2/U3/X/X2. --- arch/arm/boot/dts/exynos4412-odroid-common.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index e24aee9c0bc993..c043b1a528d414 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -464,6 +464,10 @@ pinctrl-0 = <&i2c2_bus>; }; + isp-power-domain@10023CA0 { + domain-always-on; + }; + i2c@138E0000 { status = "okay"; }; From a5bda82ca7111792f35104fe65c5f1344c3801e5 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Mon, 9 Feb 2015 08:25:41 +0100 Subject: [PATCH 693/788] exynos: pmu: use PS_HOLD based poweroff for all supported SoCs PS_HOLD based power off procedure is common for all Exynos SoCs, so use it for every Exynos SoC. Signed-off-by: Marek Szyprowski Tested-by: Krzysztof Kozlowski Reviewed-by: Krzysztof Kozlowski --- arch/arm/mach-exynos/pmu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-exynos/pmu.c b/arch/arm/mach-exynos/pmu.c index c15761ca2f187f..e812c1c85624c5 100644 --- a/arch/arm/mach-exynos/pmu.c +++ b/arch/arm/mach-exynos/pmu.c @@ -681,7 +681,7 @@ static unsigned int const exynos5420_list_disable_pmu_reg[] = { EXYNOS5420_CMU_RESET_FSYS_SYS_PWR_REG, }; -static void exynos5_power_off(void) +static void exynos_power_off(void) { unsigned int tmp; @@ -872,8 +872,6 @@ static void exynos5420_pmu_init(void) EXYNOS5420_ARM_INTR_SPREAD_USE_STANDBYWFI); pmu_raw_writel(0x1, EXYNOS5420_UP_SCHEDULER); - - pm_power_off = exynos5_power_off; pr_info("EXYNOS5420 PMU initialized\n"); } @@ -984,6 +982,8 @@ static int exynos_pmu_probe(struct platform_device *pdev) if (ret) dev_warn(dev, "can't register restart handler err=%d\n", ret); + pm_power_off = exynos_power_off; + dev_dbg(dev, "Exynos PMU Driver probe done\n"); return 0; } From 6f170592ffcae293d812edf8e1b837945c81333c Mon Sep 17 00:00:00 2001 From: Carlo Caione Date: Wed, 4 Feb 2015 10:23:19 +0100 Subject: [PATCH 694/788] drm/exynos: fix DMA_ATTR_NO_KERNEL_MAPPING usage The Exynos DRM driver doesn't follow the correct API when dealing with dma_{alloc, mmap, free}_attrs functions and the DMA_ATTR_NO_KERNEL_MAPPING attribute. When a IOMMU is not available and the DMA_ATTR_NO_KERNEL_MAPPING is used, the driver should use the pointer returned by dma_alloc_attr() as a cookie. The Exynos DRM driver directly uses the non-requested virtual kernel address returned by the DMA mapping subsystem. This just works now because the non-IOMMU codepath doesn't obey DMA_ATTR_NO_KERNEL_MAPPING but we need to fix it before fixing the DMA layer. Signed-off-by: Carlo Caione Acked-by: Marek Szyprowski Acked-by: Joonyoung Shim Signed-off-by: Inki Dae --- drivers/gpu/drm/exynos/exynos_drm_buf.c | 6 ++--- drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 29 +++++++---------------- drivers/gpu/drm/exynos/exynos_drm_gem.h | 2 ++ 3 files changed, 14 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_buf.c b/drivers/gpu/drm/exynos/exynos_drm_buf.c index 9c8088462c26f9..24994ba10e28af 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_buf.c +++ b/drivers/gpu/drm/exynos/exynos_drm_buf.c @@ -63,11 +63,11 @@ static int lowlevel_buffer_allocate(struct drm_device *dev, return -ENOMEM; } - buf->kvaddr = (void __iomem *)dma_alloc_attrs(dev->dev, + buf->cookie = dma_alloc_attrs(dev->dev, buf->size, &buf->dma_addr, GFP_KERNEL, &buf->dma_attrs); - if (!buf->kvaddr) { + if (!buf->cookie) { DRM_ERROR("failed to allocate buffer.\n"); ret = -ENOMEM; goto err_free; @@ -132,7 +132,7 @@ static void lowlevel_buffer_deallocate(struct drm_device *dev, buf->sgt = NULL; if (!is_drm_iommu_supported(dev)) { - dma_free_attrs(dev->dev, buf->size, buf->kvaddr, + dma_free_attrs(dev->dev, buf->size, buf->cookie, (dma_addr_t)buf->dma_addr, &buf->dma_attrs); drm_free_large(buf->pages); } else diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index e12ea90c62371e..84f8dfe1c5ec02 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -79,9 +79,9 @@ static int exynos_drm_fbdev_update(struct drm_fb_helper *helper, struct drm_framebuffer *fb) { struct fb_info *fbi = helper->fbdev; - struct drm_device *dev = helper->dev; struct exynos_drm_gem_buf *buffer; unsigned int size = fb->width * fb->height * (fb->bits_per_pixel >> 3); + unsigned int nr_pages; unsigned long offset; drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth); @@ -94,25 +94,14 @@ static int exynos_drm_fbdev_update(struct drm_fb_helper *helper, return -EFAULT; } - /* map pages with kernel virtual space. */ + nr_pages = buffer->size >> PAGE_SHIFT; + + buffer->kvaddr = (void __iomem *) vmap(buffer->pages, + nr_pages, VM_MAP, + pgprot_writecombine(PAGE_KERNEL)); if (!buffer->kvaddr) { - if (is_drm_iommu_supported(dev)) { - unsigned int nr_pages = buffer->size >> PAGE_SHIFT; - - buffer->kvaddr = (void __iomem *) vmap(buffer->pages, - nr_pages, VM_MAP, - pgprot_writecombine(PAGE_KERNEL)); - } else { - phys_addr_t dma_addr = buffer->dma_addr; - if (dma_addr) - buffer->kvaddr = (void __iomem *)phys_to_virt(dma_addr); - else - buffer->kvaddr = (void __iomem *)NULL; - } - if (!buffer->kvaddr) { - DRM_ERROR("failed to map pages to kernel space.\n"); - return -EIO; - } + DRM_ERROR("failed to map pages to kernel space.\n"); + return -EIO; } /* buffer count to framebuffer always is 1 at booting time. */ @@ -313,7 +302,7 @@ static void exynos_drm_fbdev_destroy(struct drm_device *dev, struct exynos_drm_gem_obj *exynos_gem_obj = exynos_fbd->exynos_gem_obj; struct drm_framebuffer *fb; - if (is_drm_iommu_supported(dev) && exynos_gem_obj->buffer->kvaddr) + if (exynos_gem_obj->buffer->kvaddr) vunmap(exynos_gem_obj->buffer->kvaddr); /* release drm framebuffer and real buffer */ diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.h b/drivers/gpu/drm/exynos/exynos_drm_gem.h index ec58fe9c40df58..308173cb4f0a0d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.h +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.h @@ -22,6 +22,7 @@ /* * exynos drm gem buffer structure. * + * @cookie: cookie returned by dma_alloc_attrs * @kvaddr: kernel virtual address to allocated memory region. * *userptr: user space address. * @dma_addr: bus address(accessed by dma) to allocated memory region. @@ -35,6 +36,7 @@ * VM_PFNMAP or not. */ struct exynos_drm_gem_buf { + void *cookie; void __iomem *kvaddr; unsigned long userptr; dma_addr_t dma_addr; From c47b7747c9ce0535e55e9f826d1ec7a6660154a1 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Mon, 2 Feb 2015 10:47:35 +0100 Subject: [PATCH 695/788] serial: samsung: earlycon support depends on CONFIG_SERIAL_SAMSUNG_CONSOLE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EarlyCon support depends on serial console infrastructure, so the code implementing it should depend on CONFIG_SERIAL_SAMSUNG_CONSOLE. This patch fixes the following build break: CC [M] drivers/tty/serial/samsung.o drivers/tty/serial/samsung.c:2468:1: error: expected declaration specifiers or ‘...’ before string constant drivers/tty/serial/samsung.c:2468:1: error: expected declaration specifiers or ‘...’ before ‘s3c2410_setup_earlycon’ drivers/tty/serial/samsung.c:2487:1: error: expected declaration specifiers or ‘...’ before string constant drivers/tty/serial/samsung.c:2487:1: error: expected declaration specifiers or ‘...’ before ‘s3c2412_setup_earlycon’ drivers/tty/serial/samsung.c:2488:1: error: expected declaration specifiers or ‘...’ before string constant drivers/tty/serial/samsung.c:2488:1: error: expected declaration specifiers or ‘...’ before ‘s3c2440_setup_earlycon’ drivers/tty/serial/samsung.c:2489:1: error: expected declaration specifiers or ‘...’ before string constant drivers/tty/serial/samsung.c:2489:1: error: expected declaration specifiers or ‘...’ before ‘s3c6400_setup_earlycon’ drivers/tty/serial/samsung.c:2506:1: error: expected declaration specifiers or ‘...’ before string constant drivers/tty/serial/samsung.c:2506:1: error: expected declaration specifiers or ‘...’ before ‘s5pv210_setup_earlycon’ drivers/tty/serial/samsung.c:2507:1: error: expected declaration specifiers or ‘...’ before string constant drivers/tty/serial/samsung.c:2507:1: error: expected declaration specifiers or ‘...’ before ‘exynos4210_setup_earlycon’ drivers/tty/serial/samsung.c:2468:1: warning: ‘s3c2410_setup_earlycon’ defined but not used [-Wunused-function] drivers/tty/serial/samsung.c:2487:1: warning: ‘s3c2412_setup_earlycon’ defined but not used [-Wunused-function] drivers/tty/serial/samsung.c:2488:1: warning: ‘s3c2440_setup_earlycon’ defined but not used [-Wunused-function] drivers/tty/serial/samsung.c:2489:1: warning: ‘s3c6400_setup_earlycon’ defined but not used [-Wunused-function] drivers/tty/serial/samsung.c:2506:1: warning: ‘s5pv210_setup_earlycon’ defined but not used [-Wunused-function] drivers/tty/serial/samsung.c:2507:1: warning: ‘exynos4210_setup_earlycon’ defined but not used [-Wunused-function] make[3]: *** [drivers/tty/serial/samsung.o] Error 1 Reported-by: kbuild test robot Signed-off-by: Marek Szyprowski --- drivers/tty/serial/samsung.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 998b956ec27e28..0d7042c27640de 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -1857,6 +1857,7 @@ static struct platform_driver samsung_serial_driver = { module_platform_driver(samsung_serial_driver); +#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE /* * Early console. */ @@ -1959,6 +1960,7 @@ OF_EARLYCON_DECLARE(exynos4210, "samsung,exynos4210-uart", s5pv210_early_console_setup); EARLYCON_DECLARE(s5pv210, s5pv210_early_console_setup); EARLYCON_DECLARE(exynos4210, s5pv210_early_console_setup); +#endif MODULE_ALIAS("platform:samsung-uart"); MODULE_DESCRIPTION("Samsung SoC Serial port driver"); From f324633eb4bf77905f24b34dc8d33873e12184e6 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 6 Feb 2015 17:59:01 +0100 Subject: [PATCH 696/788] thermal: Provide stub for thermal_of_cooling_device_register() function Odroid U3 fan can work without being registered as OF cooling device (with CONFIG_THERMAL_OF disabled). In this situation it can be controlled via PWM entry at /sys/class/hwmon/hwmon0/pwm1. Therefore, the thermal_of_cooling_device_register() function needs a stub to allow clean compilation. Signed-off-by: Lukasz Majewski --- include/linux/thermal.h | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/include/linux/thermal.h b/include/linux/thermal.h index fc52e307efab87..eacf2ded8d3381 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -293,6 +293,20 @@ struct thermal_trip { }; /* Function declarations */ +#ifdef CONFIG_THERMAL +struct thermal_cooling_device * +thermal_of_cooling_device_register(struct device_node *np, + char *type, void *devdata, + const struct thermal_cooling_device_ops *); +#else +static inline struct thermal_cooling_device * +thermal_of_cooling_device_register(struct device_node *np, + char *type, void *devdata, + const struct thermal_cooling_device_ops *ops) +{ + return NULL; +} +#endif #ifdef CONFIG_THERMAL_OF struct thermal_zone_device * thermal_zone_of_sensor_register(struct device *dev, int id, void *data, @@ -328,9 +342,6 @@ void thermal_zone_device_update(struct thermal_zone_device *); struct thermal_cooling_device *thermal_cooling_device_register(char *, void *, const struct thermal_cooling_device_ops *); -struct thermal_cooling_device * -thermal_of_cooling_device_register(struct device_node *np, char *, void *, - const struct thermal_cooling_device_ops *); void thermal_cooling_device_unregister(struct thermal_cooling_device *); struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name); int thermal_zone_get_temp(struct thermal_zone_device *tz, unsigned long *temp); From 0692732ff08b8b12440cbd5bd95ad0d48adc5c5a Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 6 Feb 2015 17:59:02 +0100 Subject: [PATCH 697/788] thermal: Provide stub for thermal_cdev_update() function Odroid U3 fan can work without being registered as OF cooling device (with CONFIG_THERMAL{_OF|} disabled). In this situation it can be controlled via PWM entry at /sys/class/hwmon/hwmon0/pwm1. Therefore, the thermal_cdev_update() function needs a stub to allow clean compilation. Signed-off-by: Lukasz Majewski --- include/linux/thermal.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/linux/thermal.h b/include/linux/thermal.h index eacf2ded8d3381..25382e68bbb193 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -298,6 +298,7 @@ struct thermal_cooling_device * thermal_of_cooling_device_register(struct device_node *np, char *type, void *devdata, const struct thermal_cooling_device_ops *); +void thermal_cdev_update(struct thermal_cooling_device *); #else static inline struct thermal_cooling_device * thermal_of_cooling_device_register(struct device_node *np, @@ -306,6 +307,9 @@ thermal_of_cooling_device_register(struct device_node *np, { return NULL; } +static inline void thermal_cdev_update(struct thermal_cooling_device *cdev) +{ +} #endif #ifdef CONFIG_THERMAL_OF struct thermal_zone_device * @@ -349,7 +353,6 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, unsigned long *temp); int get_tz_trend(struct thermal_zone_device *, int); struct thermal_instance *get_thermal_instance(struct thermal_zone_device *, struct thermal_cooling_device *, int); -void thermal_cdev_update(struct thermal_cooling_device *); void thermal_notify_framework(struct thermal_zone_device *, int); #ifdef CONFIG_NET From 752d878ed966dd38edce6587fbb5787bb5a58717 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 6 Feb 2015 17:59:03 +0100 Subject: [PATCH 698/788] Documentation: dts: Documentation entry to explain how to use PWM FAN as a cooling device Explanation of several properties, which allow PWM fan working as a cooling device, have been embraced in this commit. Signed-off-by: Lukasz Majewski --- .../devicetree/bindings/hwmon/pwm-fan.txt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt index 610757ce449213..d53fe0c11abc48 100644 --- a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt +++ b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt @@ -3,10 +3,30 @@ Bindings for a fan connected to the PWM lines Required properties: - compatible : "pwm-fan" - pwms : the PWM that is used to control the PWM fan +- cooling-levels : PWM duty cycle values in a range from 0 to 255 + which correspond to thermal cooling states + +Thorough description of the following bindings: + cooling-min-state = <0>; + cooling-max-state = <3>; + #cooling-cells = <2>; + thermal-zone { + cpu_thermal: cpu-thermal { + cooling-maps { + map0 { + trip = <&cpu_alert1>; + cooling-device = <&fan0 0 1>; + }; + }; + }; + +for PWM FAN used as cooling device can be found at: +./Documentation/devicetree/bindings/thermal/thermal.txt Example: pwm-fan { compatible = "pwm-fan"; status = "okay"; pwms = <&pwm 0 10000 0>; + cooling-levels = <0 102 170 230>; }; From cf1ac49e7fb36d0baf62637859b2f19e2763fff7 Mon Sep 17 00:00:00 2001 From: Kamil Debski Date: Fri, 6 Feb 2015 17:59:04 +0100 Subject: [PATCH 699/788] ARM: dts: Add pwm-fan node to the Odroid-U3 board Add pwm-fan node to the Odroid-U3 board file to enable PWM control of the cooling fan. In addition, add the "pwm" label to the pwm@139D0000 node in the exynos4412.dtsi. Signed-off-by: Kamil Debski [Rebased on the newest mainline by l.majewski@samsung.com] --- arch/arm/boot/dts/exynos4412-odroidu3.dts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts b/arch/arm/boot/dts/exynos4412-odroidu3.dts index b7a1e47430d778..43519c87349ebd 100644 --- a/arch/arm/boot/dts/exynos4412-odroidu3.dts +++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts @@ -50,6 +50,19 @@ pinctrl-0 = <&hdmi_cec>; status = "okay"; }; + + pwm-fan { + compatible = "pwm-fan"; + pwms = <&pwm 0 10000 0>; + status = "okay"; + }; +}; + +&pwm { + pinctrl-0 = <&pwm0_out>; + pinctrl-names = "default"; + samsung,pwm-outputs = <0>; + status = "okay"; }; &pwm { From 39c29a8ff9b3ea8edc093627d5c07dc492b013ac Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 6 Feb 2015 17:59:05 +0100 Subject: [PATCH 700/788] ARM: dts: Add properties to use pwm-fan device as a cooling device in Odroid U3 With those bindings it is possible to use pwm-fan device available in Odroid U3 as a cooling device. Signed-off-by: Lukasz Majewski --- arch/arm/boot/dts/exynos4412-odroidu3.dts | 33 ++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts b/arch/arm/boot/dts/exynos4412-odroidu3.dts index 43519c87349ebd..c9a34be70aed79 100644 --- a/arch/arm/boot/dts/exynos4412-odroidu3.dts +++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts @@ -51,11 +51,42 @@ status = "okay"; }; - pwm-fan { + fan0: pwm-fan { compatible = "pwm-fan"; pwms = <&pwm 0 10000 0>; + cooling-min-state = <0>; + cooling-max-state = <3>; + #cooling-cells = <2>; + cooling-levels = <0 102 170 230>; status = "okay"; }; + + thermal-zones { + cpu_thermal: cpu-thermal { + cooling-maps { + map0 { + trip = <&cpu_alert1>; + cooling-device = <&cpu0 7 7>; + }; + map1 { + trip = <&cpu_alert2>; + cooling-device = <&cpu0 13 13>; + }; + map2 { + trip = <&cpu_alert0>; + cooling-device = <&fan0 0 1>; + }; + map3 { + trip = <&cpu_alert1>; + cooling-device = <&fan0 1 2>; + }; + map4 { + trip = <&cpu_alert2>; + cooling-device = <&fan0 2 3>; + }; + }; + }; + }; }; &pwm { From 62eaac764068def322a46d614f1e69e75eb8b0a5 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 6 Feb 2015 17:59:06 +0100 Subject: [PATCH 701/788] hwmon: pwm-fan: Extract __set_pwm() function to only modify PWM duty cycle It was necessary to decouple code handling writing to sysfs from the one responsible for setting PWM of the fan. Due to that, new __set_pwm() method was extracted, which is responsible for only setting new PWM duty cycle. Signed-off-by: Lukasz Majewski --- drivers/hwmon/pwm-fan.c | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index 1991d9032c3843..870e1009a71716 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -33,21 +33,15 @@ struct pwm_fan_ctx { unsigned char pwm_value; }; -static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm) { - struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); - unsigned long pwm, duty; - ssize_t ret; - - if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM) - return -EINVAL; - - mutex_lock(&ctx->lock); + unsigned long duty; + int ret; if (ctx->pwm_value == pwm) - goto exit_set_pwm_no_change; + return 0; + mutex_lock(&ctx->lock); if (pwm == 0) { pwm_disable(ctx->pwm); goto exit_set_pwm; @@ -66,13 +60,28 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, exit_set_pwm: ctx->pwm_value = pwm; -exit_set_pwm_no_change: - ret = count; exit_set_pwm_err: mutex_unlock(&ctx->lock); return ret; } +static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); + unsigned long pwm; + int ret; + + if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM) + return -EINVAL; + + ret = __set_pwm(ctx, pwm); + if (ret) + return ret; + + return count; +} + static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, char *buf) { From 43516344efafccc5385d374ed393a99e2a51e616 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 6 Feb 2015 17:59:07 +0100 Subject: [PATCH 702/788] hwmon: pwm-fan: Read PWM FAN configuration from device tree This patch provides code for reading PWM FAN configuration data via device tree. The pwm-fan can work with full speed when configuration is not provided. However, errors are propagated when wrong DT bindings are found. Additionally the struct pwm_fan_ctx has been extended. Signed-off-by: Lukasz Majewski --- drivers/hwmon/pwm-fan.c | 54 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index 870e1009a71716..f3f5843b02b341 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -30,7 +30,10 @@ struct pwm_fan_ctx { struct mutex lock; struct pwm_device *pwm; - unsigned char pwm_value; + unsigned int pwm_value; + unsigned int pwm_fan_state; + unsigned int pwm_fan_max_state; + unsigned int *pwm_fan_cooling_levels; }; static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm) @@ -100,6 +103,50 @@ static struct attribute *pwm_fan_attrs[] = { ATTRIBUTE_GROUPS(pwm_fan); +int pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx *ctx) +{ + struct device_node *np = dev->of_node; + int num, i, ret; + + ret = of_property_count_elems_of_size(np, "cooling-levels", + sizeof(u32)); + + if (ret == -EINVAL) { + dev_err(dev, "Property 'cooling-levels' not found\n"); + return 0; + } + + if (ret <= 0) { + dev_err(dev, "Wrong data!\n"); + return ret; + } + + num = ret; + ctx->pwm_fan_cooling_levels = devm_kzalloc(dev, num * sizeof(u32), + GFP_KERNEL); + if (!ctx->pwm_fan_cooling_levels) + return -ENOMEM; + + ret = of_property_read_u32_array(np, "cooling-levels", + ctx->pwm_fan_cooling_levels, num); + if (ret) { + dev_err(dev, "Property 'cooling-levels' cannot be read!\n"); + return ret; + } + + for (i = 0; i < num; i++) { + if (ctx->pwm_fan_cooling_levels[i] > MAX_PWM) { + dev_err(dev, "PWM fan state[%d]:%d > %d\n", i, + ctx->pwm_fan_cooling_levels[i], MAX_PWM); + return -EINVAL; + } + } + + ctx->pwm_fan_max_state = num - 1; + + return 0; +} + static int pwm_fan_probe(struct platform_device *pdev) { struct device *hwmon; @@ -145,6 +192,11 @@ static int pwm_fan_probe(struct platform_device *pdev) pwm_disable(ctx->pwm); return PTR_ERR(hwmon); } + + ret = pwm_fan_of_get_cooling_data(&pdev->dev, ctx); + if (ret) + return ret; + return 0; } From 7cde076576754172ff7898f605af91e525adad49 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 6 Feb 2015 17:59:08 +0100 Subject: [PATCH 703/788] hwmon: pwm-fan: Code for using PWM FAN as a cooling device The PWM FAN device can now be used as a thermal cooling device. Necessary infrastructure has been added in this commit. Signed-off-by: Lukasz Majewski --- drivers/hwmon/pwm-fan.c | 86 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index f3f5843b02b341..79fe24e2bdba92 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -24,6 +24,7 @@ #include #include #include +#include #define MAX_PWM 255 @@ -68,6 +69,17 @@ static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm) return ret; } +static void pwm_fan_update_state(struct pwm_fan_ctx *ctx, unsigned long pwm) +{ + int i; + + for (i = 0; i < ctx->pwm_fan_max_state; ++i) + if (pwm < ctx->pwm_fan_cooling_levels[i + 1]) + break; + + ctx->pwm_fan_state = i; +} + static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -82,6 +94,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, if (ret) return ret; + pwm_fan_update_state(ctx, pwm); return count; } @@ -103,6 +116,59 @@ static struct attribute *pwm_fan_attrs[] = { ATTRIBUTE_GROUPS(pwm_fan); +/* thermal cooling device callbacks */ +static int pwm_fan_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct pwm_fan_ctx *ctx = cdev->devdata; + + *state = ctx->pwm_fan_max_state; + + return 0; +} + +static int pwm_fan_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct pwm_fan_ctx *ctx = cdev->devdata; + + if (!ctx) + return -EINVAL; + + *state = ctx->pwm_fan_state; + + return 0; +} + +static int +pwm_fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) +{ + struct pwm_fan_ctx *ctx = cdev->devdata; + int ret; + + if (!ctx || (state > ctx->pwm_fan_max_state)) + return -EINVAL; + + if (state == ctx->pwm_fan_state) + return 0; + + ret = __set_pwm(ctx, ctx->pwm_fan_cooling_levels[state]); + if (ret) { + dev_err(&cdev->device, "Cannot set pwm!\n"); + return ret; + } + + ctx->pwm_fan_state = state; + + return ret; +} + +static const struct thermal_cooling_device_ops pwm_fan_cooling_ops = { + .get_max_state = pwm_fan_get_max_state, + .get_cur_state = pwm_fan_get_cur_state, + .set_cur_state = pwm_fan_set_cur_state, +}; + int pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx *ctx) { struct device_node *np = dev->of_node; @@ -113,7 +179,7 @@ int pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx *ctx) if (ret == -EINVAL) { dev_err(dev, "Property 'cooling-levels' not found\n"); - return 0; + return ret; } if (ret <= 0) { @@ -149,8 +215,9 @@ int pwm_fan_of_get_cooling_data(struct device *dev, struct pwm_fan_ctx *ctx) static int pwm_fan_probe(struct platform_device *pdev) { - struct device *hwmon; + struct thermal_cooling_device *cdev; struct pwm_fan_ctx *ctx; + struct device *hwmon; int duty_cycle; int ret; @@ -194,8 +261,21 @@ static int pwm_fan_probe(struct platform_device *pdev) } ret = pwm_fan_of_get_cooling_data(&pdev->dev, ctx); - if (ret) + if (!ret) { + ctx->pwm_fan_state = ctx->pwm_fan_max_state; + cdev = thermal_of_cooling_device_register(pdev->dev.of_node, + "pwm-fan", ctx, + &pwm_fan_cooling_ops); + if (IS_ERR(cdev)) { + dev_err(&pdev->dev, + "Failed to register pwm-fan as cooling device"); + pwm_disable(ctx->pwm); + return PTR_ERR(cdev); + } + thermal_cdev_update(cdev); + } else if (ret != -EINVAL) { return ret; + } return 0; } From c3b1f5634e5fe4790126b90911a2f401f0c04812 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Mon, 9 Feb 2015 18:38:43 +0100 Subject: [PATCH 704/788] patchset: [PATCH v3 0/8] hwmon: thermal: Odroid U3: Provide support for Odroid U3 fan --- PATCHES | 1 + 1 file changed, 1 insertion(+) diff --git a/PATCHES b/PATCHES index 05174472429186..88397e66727d3f 100644 --- a/PATCHES +++ b/PATCHES @@ -6,3 +6,4 @@ [PATCH v5 00/18] Exynos SYSMMU (IOMMU) integration with DT and DMA-mapping subsystem [PATCH v4 0/2] serial: samsung: add support for early console [RFC v2 0/7] HDMI CEC framework +[PATCH v3 0/8] hwmon: thermal: Odroid U3: Provide support for Odroid U3 fan From 51e97879704c9ae5ada3edab37b1c6fdb41ea211 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Mon, 19 Jan 2015 15:32:45 +0100 Subject: [PATCH 705/788] ARM: DTS: Exynos: add 'dr_mode' property to hsotg devices All currently supported boards use hsotg/dwc2 controller in device ('peripheral') mode, so add property which sets correct operation mode. This patch fixes support in recent changes in dwc2 driver, which added support for dual-role devices. Suggested-by: Paul Zimmerman Signed-off-by: Marek Szyprowski --- arch/arm/boot/dts/exynos4210-trats.dts | 1 + arch/arm/boot/dts/exynos4210-universal_c210.dts | 1 + arch/arm/boot/dts/exynos4412-odroid-common.dtsi | 1 + arch/arm/boot/dts/exynos4412-trats2.dts | 1 + arch/arm/boot/dts/s5pv210-aquila.dts | 1 + arch/arm/boot/dts/s5pv210-goni.dts | 1 + arch/arm/boot/dts/s5pv210-smdkv210.dts | 1 + 7 files changed, 7 insertions(+) diff --git a/arch/arm/boot/dts/exynos4210-trats.dts b/arch/arm/boot/dts/exynos4210-trats.dts index 902d08c2ef4eaa..32c5fd8f6269d9 100644 --- a/arch/arm/boot/dts/exynos4210-trats.dts +++ b/arch/arm/boot/dts/exynos4210-trats.dts @@ -92,6 +92,7 @@ hsotg@12480000 { vusb_d-supply = <&vusb_reg>; vusb_a-supply = <&vusbdac_reg>; + dr_mode = "peripheral"; status = "okay"; }; diff --git a/arch/arm/boot/dts/exynos4210-universal_c210.dts b/arch/arm/boot/dts/exynos4210-universal_c210.dts index d4fbc0799db4de..d4f2b11319dd10 100644 --- a/arch/arm/boot/dts/exynos4210-universal_c210.dts +++ b/arch/arm/boot/dts/exynos4210-universal_c210.dts @@ -72,6 +72,7 @@ hsotg@12480000 { vusb_d-supply = <&ldo3_reg>; vusb_a-supply = <&ldo8_reg>; + dr_mode = "peripheral"; status = "okay"; }; diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index c043b1a528d414..6f3e0c5f9224be 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -434,6 +434,7 @@ }; hsotg@12480000 { + dr_mode = "peripheral"; status = "okay"; vusb_d-supply = <&ldo15_reg>; vusb_a-supply = <&ldo12_reg>; diff --git a/arch/arm/boot/dts/exynos4412-trats2.dts b/arch/arm/boot/dts/exynos4412-trats2.dts index fc81d39d1dbdfd..1f784c72e49a90 100644 --- a/arch/arm/boot/dts/exynos4412-trats2.dts +++ b/arch/arm/boot/dts/exynos4412-trats2.dts @@ -846,6 +846,7 @@ hsotg@12480000 { vusb_d-supply = <&ldo15_reg>; vusb_a-supply = <&ldo12_reg>; + dr_mode = "peripheral"; status = "okay"; }; diff --git a/arch/arm/boot/dts/s5pv210-aquila.dts b/arch/arm/boot/dts/s5pv210-aquila.dts index aa31b84a707acc..f00cea7aca2fa6 100644 --- a/arch/arm/boot/dts/s5pv210-aquila.dts +++ b/arch/arm/boot/dts/s5pv210-aquila.dts @@ -355,6 +355,7 @@ &hsotg { vusb_a-supply = <&ldo3_reg>; vusb_d-supply = <&ldo8_reg>; + dr_mode = "peripheral"; status = "okay"; }; diff --git a/arch/arm/boot/dts/s5pv210-goni.dts b/arch/arm/boot/dts/s5pv210-goni.dts index 6387c77a6f7bd1..a3d4643b202e75 100644 --- a/arch/arm/boot/dts/s5pv210-goni.dts +++ b/arch/arm/boot/dts/s5pv210-goni.dts @@ -333,6 +333,7 @@ &hsotg { vusb_a-supply = <&ldo3_reg>; vusb_d-supply = <&ldo8_reg>; + dr_mode = "peripheral"; status = "okay"; }; diff --git a/arch/arm/boot/dts/s5pv210-smdkv210.dts b/arch/arm/boot/dts/s5pv210-smdkv210.dts index cb8521899ec847..da7d210df6704d 100644 --- a/arch/arm/boot/dts/s5pv210-smdkv210.dts +++ b/arch/arm/boot/dts/s5pv210-smdkv210.dts @@ -181,6 +181,7 @@ }; &hsotg { + dr_mode = "peripheral"; status = "okay"; }; From 9fb44235f246b62b54b92e7e2b4b1567d7f8f37f Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Mon, 19 Jan 2015 15:44:36 +0100 Subject: [PATCH 706/788] usb: dwc2: rework initialization of host and gadget in dual-role mode If device is configured to work only in HOST or DEVICE mode, there is no point in initializing both subdrivers. This patch also fixes resource leakage if host subdriver fails to initialize. Signed-off-by: Marek Szyprowski --- drivers/usb/dwc2/core.h | 2 ++ drivers/usb/dwc2/platform.c | 29 +++++++++++++++++++++-------- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 7a70a1349334d6..f93b06daef97c0 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -570,6 +570,8 @@ struct dwc2_hsotg { struct dwc2_core_params *core_params; enum usb_otg_state op_state; enum usb_dr_mode dr_mode; + unsigned int hcd_enabled:1; + unsigned int gadget_enabled:1; struct phy *phy; struct usb_phy *uphy; diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 6a795aa2ff05ef..ee0b0b06d0fc60 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -121,8 +121,10 @@ static int dwc2_driver_remove(struct platform_device *dev) { struct dwc2_hsotg *hsotg = platform_get_drvdata(dev); - dwc2_hcd_remove(hsotg); - s3c_hsotg_remove(hsotg); + if (hsotg->hcd_enabled) + dwc2_hcd_remove(hsotg); + if (hsotg->gadget_enabled) + s3c_hsotg_remove(hsotg); return 0; } @@ -214,12 +216,23 @@ static int dwc2_driver_probe(struct platform_device *dev) spin_lock_init(&hsotg->lock); mutex_init(&hsotg->init_mutex); - retval = dwc2_gadget_init(hsotg, irq); - if (retval) - return retval; - retval = dwc2_hcd_init(hsotg, irq, params); - if (retval) - return retval; + + if (hsotg->dr_mode != USB_DR_MODE_HOST) { + retval = dwc2_gadget_init(hsotg, irq); + if (retval) + return retval; + hsotg->gadget_enabled = 1; + } + + if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) { + retval = dwc2_hcd_init(hsotg, irq, params); + if (retval) { + if (hsotg->gadget_enabled) + s3c_hsotg_remove(hsotg); + return retval; + } + hsotg->hcd_enabled = 1; + } platform_set_drvdata(dev, hsotg); From f21d08ca401d1f97cfdd01046c9c115fe7ff40ca Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Thu, 15 Jan 2015 10:50:49 +0900 Subject: [PATCH 707/788] devfreq: exynos: Add generic exynos memory bus frequency driver This patch adds the generic exynos bus frequency driver for memory bus with DEVFREQ framework. The Samsung Exynos SoCs have the common architecture for memory bus between DRAM memory and MMC/sub IP in SoC. This driver can support the memory bus frequency driver for Exynos SoCs. Each memory bus block has a clock for memory bus speed and frequency table which is changed according to the utilization of memory bus on runtime. And then each memory bus group has the one more memory bus blocks and OPP table (including frequency and voltage), regulator, devfreq-event devices. There are a little difference about the number of memory bus because each Exynos SoC have the different sub-IP and different memory bus speed. In spite of this difference among Exynos SoCs, we can support almost Exynos SoC by adding unique data of memory bus to devicetree file. Cc: Myungjoo Ham Cc: Kyungmin Park Cc: Kukjin Kim Signed-off-by: Chanwoo Choi --- drivers/devfreq/Kconfig | 15 + drivers/devfreq/Makefile | 1 + drivers/devfreq/exynos/Makefile | 1 + drivers/devfreq/exynos/exynos-bus.c | 598 ++++++++++++++++++++++++++++ 4 files changed, 615 insertions(+) create mode 100644 drivers/devfreq/exynos/exynos-bus.c diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index 21f8f17aca5f18..cb66867b0377ec 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -65,6 +65,21 @@ config DEVFREQ_GOV_USERSPACE comment "DEVFREQ Drivers" +config ARM_EXYNOS_BUS_DEVFREQ + bool "ARM EXYNOS Generic Memory Bus DEVFREQ Driver" + depends on ARCH_EXYNOS + select DEVFREQ_GOV_SIMPLE_ONDEMAND + select DEVFREQ_EVENT_EXYNOS_PPMU + select PM_DEVFREQ_EVENT + select PM_OPP + help + This adds the common DEVFREQ driver for Exynos Memory bus. Exynos + Memory bus has one more group of memory bus (e.g, MIF and INT block). + Each memory bus group could contain many memoby bus block. It reads + PPMU counters of memory controllers by using DEVFREQ-event device + and adjusts the operating frequencies and voltages with OPP support. + This does not yet operate with optimal voltages. + config ARM_EXYNOS4_BUS_DEVFREQ bool "ARM Exynos4210/4212/4412 Memory Bus DEVFREQ Driver" depends on (CPU_EXYNOS4210 || SOC_EXYNOS4212 || SOC_EXYNOS4412) && !ARCH_MULTIPLATFORM diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index c449336ed56502..29a04c5e4d84f4 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE) += governor_powersave.o obj-$(CONFIG_DEVFREQ_GOV_USERSPACE) += governor_userspace.o # DEVFREQ Drivers +obj-$(CONFIG_ARCH_EXYNOS) += exynos/ obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ) += exynos/ obj-$(CONFIG_ARM_EXYNOS5_BUS_DEVFREQ) += exynos/ diff --git a/drivers/devfreq/exynos/Makefile b/drivers/devfreq/exynos/Makefile index 49bc9175f92353..4ec06d3229961b 100644 --- a/drivers/devfreq/exynos/Makefile +++ b/drivers/devfreq/exynos/Makefile @@ -1,3 +1,4 @@ # Exynos DEVFREQ Drivers +obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ) += exynos_ppmu.o exynos4_bus.o obj-$(CONFIG_ARM_EXYNOS5_BUS_DEVFREQ) += exynos_ppmu.o exynos5_bus.o diff --git a/drivers/devfreq/exynos/exynos-bus.c b/drivers/devfreq/exynos/exynos-bus.c new file mode 100644 index 00000000000000..b34895672679be --- /dev/null +++ b/drivers/devfreq/exynos/exynos-bus.c @@ -0,0 +1,598 @@ +/* + * Generic Exynos Memory Bus Frequency driver with DEVFREQ Framework + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * Author : Chanwoo Choi + * + * This driver is based on exynos4_bus.c, which was written + * by MyungJoo Ham , Samsung Electronics. + * + * This driver support Exynos Memory Bus frequency feature by using in DEVFREQ + * framework. This version supprots Exynos3250/Exynos4 series/Exynos5260 SoC. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_SATURATION_RATIO 40 +#define SAFEVOLT 50000 + +struct exynos_memory_bus_opp { + unsigned long rate; + unsigned long volt; +}; + +struct exynos_memory_bus_block { + struct clk *clk; + struct exynos_memory_bus_opp *freq_table; +}; + +struct exynos_memory_bus { + /* devfreq device to monitor and control memory bus group */ + struct device *dev; + struct devfreq *devfreq; + + struct exynos_memory_bus_opp *freq_table; + unsigned int freq_count; + struct regulator *regulator; + struct mutex lock; + int ratio; + + struct exynos_memory_bus_opp curr_opp; + + struct exynos_memory_bus_block *block; + unsigned int block_count; + + /* devfreq-event device to get current state of memory bus group */ + struct devfreq_event_dev **edev; + unsigned int edev_count; +}; + +/* + * Initialize the memory bus group/block by parsing dt node in the devicetree + */ +static int of_init_memory_bus(struct device_node *np, + struct exynos_memory_bus *bus) +{ + struct device *dev = bus->dev; + struct dev_pm_opp *opp; + unsigned long rate, volt; + int i, ret, count, size; + + /* Get the freq/voltage OPP table to scale memory bus frequency */ + ret = of_init_opp_table(dev); + if (ret < 0) { + dev_err(dev, "failed to get OPP table\n"); + return ret; + } + + rcu_read_lock(); + + bus->freq_count = dev_pm_opp_get_opp_count(dev); + if (bus->freq_count <= 0) { + dev_err(dev, "failed to get the count of OPP entry\n"); + rcu_read_unlock(); + return -EINVAL; + } + + size = sizeof(*bus->freq_table) * bus->freq_count; + bus->freq_table = devm_kzalloc(dev, size, GFP_KERNEL); + if (!bus->freq_table) { + rcu_read_unlock(); + return -ENOMEM; + } + + for (i = 0, rate = 0; i < bus->freq_count; i++, rate++) { + opp = dev_pm_opp_find_freq_ceil(dev, &rate); + if (IS_ERR(opp)) { + dev_err(dev, "failed to find dev_pm_opp\n"); + rcu_read_unlock(); + return PTR_ERR(opp); + } + + volt = dev_pm_opp_get_voltage(opp); + + bus->freq_table[i].rate = rate; + bus->freq_table[i].volt = volt; + + dev_dbg(dev, "Level%d : freq(%ld), voltage(%ld)\n", i, rate, volt); + } + + rcu_read_unlock(); + + /* Get the regulator to provide memory bus group with the power */ + bus->regulator = devm_regulator_get(dev, "vdd-mem"); + if (IS_ERR(bus->regulator)) { + dev_err(dev, "failed to get vdd-memory regulator\n"); + return PTR_ERR(bus->regulator); + } + + ret = regulator_enable(bus->regulator); + if (ret < 0) { + dev_err(dev, "failed to enable vdd-memory regulator\n"); + return ret; + } + + /* Get the saturation ratio according to Exynos SoC */ + if (of_property_read_u32(np, "exynos,saturation-ratio", &bus->ratio)) + bus->ratio = DEFAULT_SATURATION_RATIO; + + /* + * Get the devfreq-event devices to get the current state of + * memory bus group. This raw data will be used in devfreq governor. + */ + count = devfreq_event_get_edev_count(dev); + if (count < 0) { + dev_err(dev, "failed to get the count of devfreq-event dev\n"); + return count; + } + bus->edev_count = count; + + size = sizeof(*bus->edev) * count; + bus->edev = devm_kzalloc(dev, size, GFP_KERNEL); + if (!bus->edev) + return -ENOMEM; + + for (i = 0; i < count; i++) { + bus->edev[i] = devfreq_event_get_edev_by_phandle(dev, i); + if (IS_ERR(bus->edev[i])) { + of_free_opp_table(dev); + return -EPROBE_DEFER; + } + } + + return 0; +} + +static int of_init_memory_bus_block(struct device_node *np, + struct exynos_memory_bus *bus) +{ + struct exynos_memory_bus_block *block; + struct device *dev = bus->dev; + struct device_node *buses_np, *node; + int i, count; + + buses_np = of_get_child_by_name(np, "blocks"); + if (!buses_np) { + dev_err(dev, + "failed to get child node of memory bus\n"); + return -EINVAL; + } + + count = of_get_child_count(buses_np); + block = devm_kzalloc(dev, sizeof(*block) * count, GFP_KERNEL); + if (!block) + return -ENOMEM; + bus->block = block; + bus->block_count = count; + + /* Parse the busrmation of memory bus block */ + i = 0; + for_each_child_of_node(buses_np, node) { + const struct property *prop; + const __be32 *val; + int j, nr, size; + + block = &bus->block[i++]; + + /* Get the frequency table of each memory bus block */ + prop = of_find_property(node, "frequency", NULL); + if (!prop) + return -ENODEV; + if (!prop->value) + return -ENODATA; + + nr = prop->length / sizeof(u32); + if (!nr) + return -EINVAL; + + if (nr != bus->freq_count) { + dev_err(dev, "the size of frequency table is different \ + from OPP table\n"); + return -EINVAL; + } + + size = sizeof(*block->freq_table) * nr; + block->freq_table = devm_kzalloc(dev, size, GFP_KERNEL); + if (!block->freq_table) + return -ENOMEM; + + val = prop->value; + for (j = nr - 1; j >= 0; j--) + block->freq_table[j].rate = be32_to_cpup(val++) * 1000; + + for (j = 0; j < nr; j++) + dev_dbg(dev, "%s: Level%d : freq(%ld)\n", + node->name, j, block->freq_table[j].rate); + + /* Get the clock of each memory bus block */ + block->clk = of_clk_get_by_name(node, "memory-bus"); + if (IS_ERR(block->clk)) { + dev_err(dev, "failed to get memory-bus clock in %s\n", + node->name); + return PTR_ERR(block->clk); + } + clk_prepare_enable(block->clk); + + of_node_put(node); + } + + of_node_put(buses_np); + + return 0; +} + +/* + * Control the devfreq-event device to get the current state of memory bus + */ +static int exynos_bus_enable_edev(struct exynos_memory_bus *bus) +{ + int i, ret; + + for (i = 0; i < bus->edev_count; i++) { + ret = devfreq_event_enable_edev(bus->edev[i]); + if (ret < 0) + return ret; + } + + return 0; +} + +static int exynos_bus_disable_edev(struct exynos_memory_bus *bus) +{ + int i, ret; + + for (i = 0; i < bus->edev_count; i++) { + ret = devfreq_event_disable_edev(bus->edev[i]); + if (ret < 0) + return ret; + } + + return 0; +} + + +static int exynos_bus_set_event(struct exynos_memory_bus *bus) +{ + int i, ret; + + for (i = 0; i < bus->edev_count; i++) { + ret = devfreq_event_set_event(bus->edev[i]); + if (ret < 0) + return ret; + } + + return 0; +} + +static int exynos_bus_get_event(struct exynos_memory_bus *bus, + struct devfreq_event_data *edata) +{ + struct devfreq_event_data event_data; + unsigned long event = 0, total_event = 0; + int i, ret = 0; + + for (i = 0; i < bus->edev_count; i++) { + ret = devfreq_event_get_event(bus->edev[i], &event_data); + if (ret < 0) + return ret; + + if (i == 0 || event_data.event > event) { + event = event_data.event; + total_event = event_data.total_event; + } + } + + edata->event = event; + edata->total_event = total_event; + + return ret; +} + +/* + * Must necessary function for devfreq governor + */ + +static int exynos_bus_set_frequency(struct exynos_memory_bus *bus, + struct exynos_memory_bus_opp *new_opp) +{ + int i, j; + + for (i = 0; i < bus->freq_count; i++) + if (new_opp->rate == bus->freq_table[i].rate) + break; + + if (i == bus->freq_count) + i = bus->freq_count - 1; + + for (j = 0; j < bus->block_count; j++) + clk_set_rate(bus->block[j].clk, + bus->block[j].freq_table[i].rate); + + return 0; +} + +static int exynos_bus_target(struct device *dev, unsigned long *freq, u32 flags) +{ + struct exynos_memory_bus *bus = dev_get_drvdata(dev); + struct exynos_memory_bus_opp new_opp; + unsigned long new_freq, old_freq; + struct dev_pm_opp *opp; + int ret = 0; + + /* Get new opp-bus instance according to new bus clock */ + rcu_read_lock(); + opp = devfreq_recommended_opp(dev, freq, flags); + if (IS_ERR_OR_NULL(opp)) { + dev_err(dev, "failed to get recommed opp instance\n"); + rcu_read_unlock(); + return PTR_ERR(opp); + } + new_opp.rate = dev_pm_opp_get_freq(opp); + new_opp.volt = dev_pm_opp_get_voltage(opp); + rcu_read_unlock(); + + old_freq = bus->curr_opp.rate; + new_freq = new_opp.rate; + if (old_freq == new_freq) + return 0; + + dev_dbg(dev, "Change the frequency of memory bus (%ld kHz -> %ld kHz)\n", + old_freq / 1000, new_freq / 1000); + + /* Change voltage/clock according to new bus level */ + mutex_lock(&bus->lock); + + if (old_freq < new_freq) { + ret = regulator_set_voltage(bus->regulator, new_opp.volt, + new_opp.volt + SAFEVOLT); + if (ret < 0) { + dev_err(bus->dev, "failed to set voltage\n"); + regulator_set_voltage(bus->regulator, + bus->curr_opp.rate, + bus->curr_opp.rate + SAFEVOLT); + goto out; + } + } + + ret = exynos_bus_set_frequency(bus, &new_opp); + if (ret < 0) { + dev_err(dev, "failed to change clock of memory bus\n"); + goto out; + } + + if (old_freq > new_freq) { + ret = regulator_set_voltage(bus->regulator, new_opp.volt, + new_opp.volt + SAFEVOLT); + if (ret < 0) { + dev_err(bus->dev, "failed to set voltage\n"); + regulator_set_voltage(bus->regulator, + bus->curr_opp.rate, + bus->curr_opp.rate + SAFEVOLT); + goto out; + } + } + + bus->curr_opp = new_opp; + +out: + mutex_unlock(&bus->lock); + + return ret; +} + +static int exynos_bus_get_dev_status(struct device *dev, + struct devfreq_dev_status *stat) +{ + struct exynos_memory_bus *bus = dev_get_drvdata(dev); + struct devfreq_event_data edata; + int ret; + + stat->current_frequency = bus->curr_opp.rate; + + ret = exynos_bus_get_event(bus, &edata); + if (ret < 0) { + stat->total_time = stat->busy_time = 0; + goto err; + } + + stat->busy_time = (edata.event * 100) / bus->ratio; + stat->total_time = edata.total_event; + + dev_dbg(dev, "Usage of devfreq-event : %ld/%ld\n", stat->busy_time, + stat->total_time); + +err: + ret = exynos_bus_set_event(bus); + if (ret < 0) { + dev_err(dev, "failed to set event to devfreq-event devices\n"); + return ret; + } + + return ret; +} + +static void exynos_bus_exit(struct device *dev) +{ + struct exynos_memory_bus *bus = dev_get_drvdata(dev); + int i, ret; + + ret = exynos_bus_disable_edev(bus); + if (ret < 0) + dev_warn(dev, "failed to disable the devfreq-event devices\n"); + + for (i = 0; i < bus->block_count; i++) + clk_disable_unprepare(bus->block[i].clk); + + if (regulator_is_enabled(bus->regulator)) + regulator_disable(bus->regulator); + + of_free_opp_table(dev); +} + +static struct devfreq_dev_profile exynos_memory_bus_profile = { + .polling_ms = 100, + .target = exynos_bus_target, + .get_dev_status = exynos_bus_get_dev_status, + .exit = exynos_bus_exit, +}; + +static struct devfreq_simple_ondemand_data exynos_memory_bus_ondemand_data = { + .upthreshold = 40, + .downdifferential = 5, +}; + +static int exynos_bus_probe(struct platform_device *pdev) +{ + struct exynos_memory_bus *bus; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + int ret; + + if (!np) { + dev_err(dev, "failed to find devicetree node\n"); + return -EINVAL; + } + + bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL); + if (!bus) + return -ENOMEM; + mutex_init(&bus->lock); + bus->dev = &pdev->dev; + platform_set_drvdata(pdev, bus); + + /* Initialize */ + ret = of_init_memory_bus(np, bus); + if (ret < 0) { + dev_err(dev, "failed to initialize memory-bus\n"); + return ret; + } + + ret = of_init_memory_bus_block(np, bus); + if (ret < 0) { + dev_err(dev, "failed to initialize memory-bus block\n"); + return ret; + } + + /* Add devfreq device for DVFS of memory bus */ + bus->devfreq = devm_devfreq_add_device(dev, + &exynos_memory_bus_profile, + "simple_ondemand", + &exynos_memory_bus_ondemand_data); + if (IS_ERR_OR_NULL(bus->devfreq)) { + dev_err(dev, "failed to add devfreq device\n"); + return PTR_ERR(bus->devfreq); + } + + /* Register opp_notifier to catch the change of OPP */ + ret = devm_devfreq_register_opp_notifier(dev, bus->devfreq); + if (ret < 0) { + dev_err(dev, "failed to register opp notifier\n"); + return ret; + } + + /* + * Enable devfreq-event to get raw data which is used to determine + * current memory bus load. + */ + ret = exynos_bus_enable_edev(bus); + if (ret < 0) { + dev_err(dev, "failed to enable devfreq-event devices\n"); + return ret; + } + + ret = exynos_bus_set_event(bus); + if (ret < 0) { + dev_err(dev, "failed to set event to devfreq-event devices\n"); + return ret; + } + + return 0; +} + +static int exynos_bus_remove(struct platform_device *pdev) +{ + /* + * devfreq_dev_profile.exit() have to free the resource of this + * device driver. + */ + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int exynos_bus_resume(struct device *dev) +{ + struct exynos_memory_bus *bus = dev_get_drvdata(dev); + int ret; + + ret = regulator_enable(bus->regulator); + if (ret < 0) { + dev_err(dev, "failed to enable vdd-memory regulator\n"); + return ret; + } + + ret = exynos_bus_enable_edev(bus); + if (ret < 0) { + dev_err(dev, "failed to enable the devfreq-event devices\n"); + return ret; + } + + return 0; +} + +static int exynos_bus_suspend(struct device *dev) +{ + struct exynos_memory_bus *bus = dev_get_drvdata(dev); + int ret; + + ret = exynos_bus_disable_edev(bus); + if (ret < 0) { + dev_err(dev, "failed to disable the devfreq-event devices\n"); + return ret; + } + + regulator_disable(bus->regulator); + + return 0; +} +#endif + +static const struct dev_pm_ops exynos_bus_pm = { + SET_SYSTEM_SLEEP_PM_OPS(exynos_bus_suspend, exynos_bus_resume) +}; + +static const struct of_device_id exynos_bus_of_match[] = { + { .compatible = "samsung,exynos-memory-bus", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, exynos_bus_of_match); + +static struct platform_driver exynos_bus_platdrv = { + .probe = exynos_bus_probe, + .remove = exynos_bus_remove, + .driver = { + .name = "exynos-memory-bus", + .owner = THIS_MODULE, + .pm = &exynos_bus_pm, + .of_match_table = of_match_ptr(exynos_bus_of_match), + }, +}; +module_platform_driver(exynos_bus_platdrv); + +MODULE_DESCRIPTION("Generic Exynos Memory Bus Frequency driver"); +MODULE_AUTHOR("Chanwoo Choi "); +MODULE_LICENSE("GPL v2"); From 4679780e0930efc0b6d0bfde23eaadf0d0196140 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Thu, 15 Jan 2015 10:50:50 +0900 Subject: [PATCH 708/788] devfreq: exynos: Add documentation for generic exynos memory bus frequency driver This patch adds the documentation for generic exynos memory bus frequency driver. Cc: MyungJoo Ham Cc: Kyungmin Park Cc: Kukjin Kim Signed-off-by: Chanwoo Choi Acked-by: MyungJoo Ham --- .../bindings/devfreq/exynos-bus.txt | 188 ++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 Documentation/devicetree/bindings/devfreq/exynos-bus.txt diff --git a/Documentation/devicetree/bindings/devfreq/exynos-bus.txt b/Documentation/devicetree/bindings/devfreq/exynos-bus.txt new file mode 100644 index 00000000000000..6323dcfc6f79fd --- /dev/null +++ b/Documentation/devicetree/bindings/devfreq/exynos-bus.txt @@ -0,0 +1,188 @@ + +* Generic Exynos Memory Bus device + +The Samsung Exynos SoCs have many memory buses for data transfer between DRAM +memory and MMC/sub-IP in SoC. Almost Exynos SoCs have the common architecture +for memory buses. Generally, Exynos SoC express the memory bus by using memory +bus group and block. The memory bus group has one more memory bus blocks and +OPP table (including frequency and voltage for DVFS), regulator, devfreq-event +devices. Each memory bus block has a clock for own memory bus speen and +frequency table for DVFS. There are a little different among Exynos SoCs +because each Exynos SoC has the different sub-IP and differnt memory bus. +So, this difference should be specified in devicetree file. + +Required properties for memory bus group: +- compatible: Should be "samsung,exynos-memory-bus". +- operating-points: the OPP table including frequency/voltage information to + support DVFS (Dynamic Voltage/Frequency Scaling) feature. +- devfreq-events: the devfreq-event device to monitor the curret state of + memory bus group. +- vdd-mem-supply: the regulator to provide memory bus group with the voltage. + +Optional properties for memory bus group: +- exynos,saturation-ratio: the percentage value which is used to calibrate + the memory performance count againt memory cycle count. + +Required properties for memory bus block: +- clock-names : the name of clock used by the memory bus, "memory-bus". +- clocks : phandles for clock specified in "clock-names" property. +- #clock-cells: should be 1. +- frequency: the frequency table to support DVFS feature. + +Example1 : Memory bus group/block in exynos3250.dtsi are listed below. + Exynos3250 has two memory bus group (MIF, INT group). MIF (Memory + Interface) memory bus group includes one memory bus block between + DRAM and eMMC. Also, INT (Internal) memory bus group includes eight + memory bus blocks which support each sub-IP between DRAM and sub-IP. + + memory_bus_mif: memory_bus@0 { + compatible = "samsung,exynos-memory-bus"; + + operating-points = < + 400000 875000 + 200000 800000 + 133000 800000 + 100000 800000 + 50000 800000>; + status = "disabled"; + + blocks { + dmc_block: memory_bus_block1 { + clocks = <&cmu_dmc CLK_DIV_DMC>; + clock-names = "memory-bus"; + frequency = < + 400000 + 200000 + 133000 + 100000 + 50000>; + }; + }; + }; + + memory_bus_int: memory_bus@1 { + compatible = "samsung,exynos-memory-bus"; + + operating-points = < + 400000 950000 + 200000 950000 + 133000 925000 + 100000 850000 + 80000 850000 + 50000 850000>; + + status = "disabled"; + + blocks { + peri_block: memory_bus_block1 { + clocks = <&cmu CLK_DIV_ACLK_100>; + clock-names = "memory-bus"; + frequency = < + 100000 + 100000 + 100000 + 100000 + 50000 + 50000>; + }; + + display_block: memory_bus_block2 { + clocks = <&cmu CLK_DIV_ACLK_160>; + clock-names = "memory-bus"; + frequency = < + 200000 + 160000 + 100000 + 80000 + 80000 + 50000>; + }; + + isp_block: memory_bus_block3 { + clocks = <&cmu CLK_DIV_ACLK_200>; + clock-names = "memory-bus"; + frequency = < + 200000 + 200000 + 100000 + 80000 + 50000 + 50000>; + }; + + gps_block: memory_bus_block4 { + clocks = <&cmu CLK_DIV_ACLK_266>; + clock-names = "memory-bus"; + frequency = < + 300000 + 200000 + 133000 + 100000 + 50000 + 50000>; + }; + + mcuisp_block: memory_bus_block5 { + clocks = <&cmu CLK_DIV_ACLK_400_MCUISP>; + clock-names = "memory-bus"; + frequency = < + 400000 + 200000 + 50000 + 50000 + 50000 + 50000>; + }; + + leftbus_block: memory_bus_block6 { + clocks = <&cmu CLK_DIV_GDL>; + clock-names = "memory-bus"; + frequency = < + 200000 + 200000 + 133000 + 100000 + 100000 + 100000>; + }; + + rightbus_block: memory_bus_block7 { + clocks = <&cmu CLK_DIV_GDR>; + clock-names = "memory-bus"; + frequency = < + 200000 + 200000 + 133000 + 100000 + 100000 + 100000>; + }; + + mfc_block: memory_bus_block8 { + clocks = <&cmu CLK_SCLK_MFC>; + clock-names = "memory-bus"; + frequency = < + 200000 + 200000 + 200000 + 133000 + 100000 + 80000>; + }; + }; + }; + +Example2 : Usage case to handle the frequency/voltage of memory bus on runtime + in exynos3250-rinato.dts are listed below. + + &memory_bus_mif { + devfreq-events = <&ppmu_dmc0_3>, <&ppmu_dmc1_3>; + vdd-mem-supply = <&buck1_reg>; + status = "okay"; + }; + + &memory_bus_int { + devfreq-events = <&ppmu_leftbus_3>, <&ppmu_rightbus_3>; + vdd-mem-supply = <&buck3_reg>; + status = "okay"; + }; From a408168625279f52e2ad7a9f684bc43ba9e2db98 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Thu, 15 Jan 2015 10:50:51 +0900 Subject: [PATCH 709/788] ARM: dts: Add memory bus node for Exynos3250 This patch adds the memory bus node for Exynos3250 SoC. Exynos3250 has following memory buses to translate data between DRAM and eMMC/sub-IPs. Following list specifies the detailed relation between memory bus clock and DMC IP in MIF (Memory Interface) block: - DMC clock : DMC (Dynamic Memory Controller) Following list specifies the detailed relation between memory bus clock and sub-IPs in INT (Internal) block: - ACLK100 clock : PERIL - ACLK160 clock : LCD0 - ACLK200 clock : FSYS - ACLK266 clock : ISP - GDL/GDR clock : leftbus/rightbus - SCLK_MFC clock : MFC Cc: Kukjin Kim Cc: Myungjoo Ham Cc: Kyungmin Park Signed-off-by: Chanwoo Choi Acked-by: Kyungmin Park Acked-by: Myungjoo Ham --- arch/arm/boot/dts/exynos3250.dtsi | 125 ++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/arch/arm/boot/dts/exynos3250.dtsi b/arch/arm/boot/dts/exynos3250.dtsi index bea31b8312e1dd..96d13f5d7d321e 100644 --- a/arch/arm/boot/dts/exynos3250.dtsi +++ b/arch/arm/boot/dts/exynos3250.dtsi @@ -100,6 +100,131 @@ }; }; + memory_bus_mif: memory_bus@0 { + compatible = "samsung,exynos-memory-bus"; + + operating-points = < + 400000 875000 + 200000 800000 + 133000 800000 + 100000 800000 + 50000 800000>; + status = "disabled"; + + blocks { + dmc_block: memory_bus_block1 { + clocks = <&cmu_dmc CLK_DIV_DMC>; + clock-names = "memory-bus"; + frequency = < + 400000 + 200000 + 133000 + 100000 + 50000>; + }; + }; + }; + + memory_bus_int: memory_bus@1 { + compatible = "samsung,exynos-memory-bus"; + + operating-points = < + 400000 950000 + 200000 950000 + 133000 925000 + 100000 850000 + 80000 850000 + 50000 850000>; + + status = "disabled"; + + blocks { + peril_block: memory_bus_block1 { + clocks = <&cmu CLK_DIV_ACLK_100>; + clock-names = "memory-bus"; + frequency = < + 100000 + 100000 + 100000 + 100000 + 50000 + 50000>; + }; + + lcd0_block: memory_bus_block2 { + clocks = <&cmu CLK_DIV_ACLK_160>; + clock-names = "memory-bus"; + frequency = < + 200000 + 160000 + 100000 + 80000 + 80000 + 50000>; + }; + + fsys_block: memory_bus_block3 { + clocks = <&cmu CLK_DIV_ACLK_200>; + clock-names = "memory-bus"; + frequency = < + 200000 + 200000 + 100000 + 80000 + 50000 + 50000>; + }; + + isp_block: memory_bus_block4 { + clocks = <&cmu CLK_DIV_ACLK_266>; + clock-names = "memory-bus"; + frequency = < + 300000 + 200000 + 133000 + 100000 + 50000 + 50000>; + }; + + leftbus_block: memory_bus_block5 { + clocks = <&cmu CLK_DIV_GDL>; + clock-names = "memory-bus"; + frequency = < + 200000 + 200000 + 133000 + 100000 + 100000 + 100000>; + }; + + rightbus_block: memory_bus_block6 { + clocks = <&cmu CLK_DIV_GDR>; + clock-names = "memory-bus"; + frequency = < + 200000 + 200000 + 133000 + 100000 + 100000 + 100000>; + }; + + mfc_block: memory_bus_block7 { + clocks = <&cmu CLK_SCLK_MFC>; + clock-names = "memory-bus"; + frequency = < + 200000 + 200000 + 200000 + 133000 + 100000 + 80000>; + }; + }; + }; + sysram@02020000 { compatible = "mmio-sram"; reg = <0x02020000 0x40000>; From fe5eb3abfedbfd741b095f1cdc5ad7a9587a402f Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Thu, 15 Jan 2015 10:50:52 +0900 Subject: [PATCH 710/788] clk: samsung: exynos4: Add divider clock id for memory bus frequency This patch adds the divider clock id for Exynos4 memory bus frequency. The clock id is used fo DVFS (Dynamic Voltage/Frequency Scaling) feature of exynos memory bus frequency. Cc: Sylwester Nawrocki Cc: Tomasz Figa Signed-off-by: Chanwoo Choi --- drivers/clk/samsung/clk-exynos4.c | 10 +++++----- include/dt-bindings/clock/exynos4.h | 7 ++++++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index cb7dd07aaca322..864b5e21ac3a7b 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -703,12 +703,12 @@ static struct samsung_mux_clock exynos4x12_mux_clks[] __initdata = { /* list of divider clocks supported in all exynos4 soc's */ static struct samsung_div_clock exynos4_div_clks[] __initdata = { - DIV(0, "div_gdl", "mout_gdl", DIV_LEFTBUS, 0, 3), + DIV(CLK_DIV_GDL, "div_gdl", "mout_gdl", DIV_LEFTBUS, 0, 3), DIV(0, "div_gpl", "div_gdl", DIV_LEFTBUS, 4, 3), DIV(0, "div_clkout_leftbus", "mout_clkout_leftbus", CLKOUT_CMU_LEFTBUS, 8, 6), - DIV(0, "div_gdr", "mout_gdr", DIV_RIGHTBUS, 0, 3), + DIV(CLK_DIV_GDR, "div_gdr", "mout_gdr", DIV_RIGHTBUS, 0, 3), DIV(0, "div_gpr", "div_gdr", DIV_RIGHTBUS, 4, 3), DIV(0, "div_clkout_rightbus", "mout_clkout_rightbus", CLKOUT_CMU_RIGHTBUS, 8, 6), @@ -781,10 +781,10 @@ static struct samsung_div_clock exynos4_div_clks[] __initdata = { CLK_SET_RATE_PARENT, 0), DIV(0, "div_clkout_top", "mout_clkout_top", CLKOUT_CMU_TOP, 8, 6), - DIV(0, "div_acp", "mout_dmc_bus", DIV_DMC0, 0, 3), + DIV(CLK_DIV_ACP, "div_acp", "mout_dmc_bus", DIV_DMC0, 0, 3), DIV(0, "div_acp_pclk", "div_acp", DIV_DMC0, 4, 3), DIV(0, "div_dphy", "mout_dphy", DIV_DMC0, 8, 3), - DIV(0, "div_dmc", "mout_dmc_bus", DIV_DMC0, 12, 3), + DIV(CLK_DIV_DMC, "div_dmc", "mout_dmc_bus", DIV_DMC0, 12, 3), DIV(0, "div_dmcd", "div_dmc", DIV_DMC0, 16, 3), DIV(0, "div_dmcp", "div_dmcd", DIV_DMC0, 20, 3), DIV(0, "div_pwi", "mout_pwi", DIV_DMC1, 8, 4), @@ -829,7 +829,7 @@ static struct samsung_div_clock exynos4x12_div_clks[] __initdata = { DIV_F(CLK_DIV_MCUISP1, "div_mcuisp1", "div_mcuisp0", E4X12_DIV_ISP1, 8, 3, CLK_GET_RATE_NOCACHE, 0), DIV(CLK_SCLK_FIMG2D, "sclk_fimg2d", "mout_g2d", DIV_DMC1, 0, 4), - DIV(0, "div_c2c", "mout_c2c", DIV_DMC1, 4, 3), + DIV(CLK_DIV_C2C, "div_c2c", "mout_c2c", DIV_DMC1, 4, 3), DIV(0, "div_c2c_aclk", "div_c2c", DIV_DMC1, 12, 3), }; diff --git a/include/dt-bindings/clock/exynos4.h b/include/dt-bindings/clock/exynos4.h index 6193f6c6ac343c..7d3e4ac9998c5d 100644 --- a/include/dt-bindings/clock/exynos4.h +++ b/include/dt-bindings/clock/exynos4.h @@ -265,8 +265,13 @@ #define CLK_DIV_MCUISP1 453 /* Exynos4x12 only */ #define CLK_DIV_ACLK200 454 /* Exynos4x12 only */ #define CLK_DIV_ACLK400_MCUISP 455 /* Exynos4x12 only */ +#define CLK_DIV_ACP 456 +#define CLK_DIV_DMC 457 +#define CLK_DIV_C2C 458 /* Exynos4x12 only */ +#define CLK_DIV_GDL 459 +#define CLK_DIV_GDR 460 /* must be greater than maximal clock id */ -#define CLK_NR_CLKS 456 +#define CLK_NR_CLKS 461 #endif /* _DT_BINDINGS_CLOCK_EXYNOS_4_H */ From e482a8b392a2294f5f67c5552a4f47d759c18cc7 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Thu, 15 Jan 2015 10:50:53 +0900 Subject: [PATCH 711/788] ARM: dts: Add memory bus node for Exynos4x12 This patch adds the memory bus node for Exynos4x12 SoC. Exynos4x12 SoC has two memory bus to translate data between DRAM and eMMC/sub-IPs. Following list specifies the detailed relation between memory bus clock and DMC IP in MIF (Memory Interface) block: - DMC/ACP clock : DMC (Dynamic Memory Controller) Following list specifies the detailed relation between memory bus clock and sub-IPs in INT (Internal) block: - ACLK100 clock : PERIL/PERIR/MFC(PCLK) - ACLK160 clock : CAM/TV/LCD - ACLK133 clock : FSYS - GDL/GDR clock : leftbus/rightbus - SCLK_MFC clock : MFC Cc: Kukjin Kim Cc: Myungjoo Ham Cc: Kyungmin Park Signed-off-by: Chanwoo Choi Acked-by: Myungjoo Ham --- arch/arm/boot/dts/exynos4x12.dtsi | 121 ++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/arch/arm/boot/dts/exynos4x12.dtsi b/arch/arm/boot/dts/exynos4x12.dtsi index 6c664bae0d9fcb..3f0071c03c4dd2 100644 --- a/arch/arm/boot/dts/exynos4x12.dtsi +++ b/arch/arm/boot/dts/exynos4x12.dtsi @@ -32,6 +32,127 @@ mshc0 = &mshc_0; }; + memory_bus_mif: memory_bus@0 { + compatible = "samsung,exynos-memory-bus"; + + operating-points = < + 400000 1100000 + 200000 1000000 + 160000 950000 + 133000 950000 + 100000 950000>; + status = "disabled"; + + blocks { + dmc_block: memory_bus_block1 { + clocks = <&clock CLK_DIV_DMC>; + clock-names = "memory-bus"; + frequency = < + 400000 + 200000 + 160000 + 133000 + 100000>; + }; + + acp_block: memory_bus_block2 { + clocks = <&clock CLK_DIV_ACP>; + clock-names = "memory-bus"; + frequency = < + 200000 + 160000 + 133000 + 133000 + 100000>; + }; + + c2c_block: memory_bus_block3 { + clocks = <&clock CLK_DIV_C2C>; + clock-names = "memory-bus"; + frequency = < + 400000 + 200000 + 160000 + 133000 + 100000>; + }; + }; + }; + + memory_bus_int: memory_bus@1 { + compatible = "samsung,exynos-memory-bus"; + + operating-points = < + 200000 1000000 + 160000 950000 + 133000 925000 + 100000 900000>; + + status = "disabled"; + + blocks { + peri_block: memory_bus_block1 { + clocks = <&clock CLK_ACLK100>; + clock-names = "memory-bus"; + frequency = < + 100000 + 100000 + 100000 + 100000>; + }; + + fsys_block: memory_bus_block2 { + clocks = <&clock CLK_ACLK133>; + clock-names = "memory-bus"; + frequency = < + 133000 + 133000 + 100000 + 100000>; + }; + + display_block: memory_bus_block3 { + clocks = <&clock CLK_ACLK160>; + clock-names = "memory-bus"; + frequency = < + 160000 + 160000 + 133000 + 100000>; + }; + + leftbus_block: memory_bus_block4 { + clocks = <&clock CLK_DIV_GDL>; + clock-names = "memory-bus"; + frequency = < + 200000 + 160000 + 133000 + 100000>; + }; + + rightbus_block: memory_bus_block5 { + clocks = <&clock CLK_DIV_GDR>; + clock-names = "memory-bus"; + frequency = < + 200000 + 160000 + 133000 + 100000>; + }; + + mfc_block: memory_bus_block6 { + clocks = <&clock CLK_SCLK_MFC>; + clock-names = "memory-bus"; + frequency = < + 200000 + 160000 + 133000 + 100000>; + }; + }; + }; + sysram@02020000 { compatible = "mmio-sram"; reg = <0x02020000 0x40000>; From db39db41d211558ef53ad64e3b325213d08794a1 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Thu, 15 Jan 2015 10:50:54 +0900 Subject: [PATCH 712/788] ARM: dts: Add memory bus node for Exynos4210 This patch adds the memory bus node for Exynos4210 SoC. Exynos4210 SoC has one memory bus to translate data between DRAM and eMMC/sub-IPs because Exynos4210 must need only one regulator for memory bus. Following list specifies the detailed relation between memory bus clock and sub-IPs: - DMC/ACP clock : DMC (Dynamic Memory Controller) - ACLK200 clock : LCD0 - ACLK100 clock : PERIL/PERIR/MFC(PCLK) - ACLK160 clock : CAM/TV/LCD0/LCD1 - ACLK133 clock : FSYS/GPS - GDL/GDR clock : leftbus/rightbus - SCLK_MFC clock : MFC Cc: Kukjin Kim Cc: Myungjoo Ham Cc: Kyungmin Park Signed-off-by: Chanwoo Choi --- arch/arm/boot/dts/exynos4210.dtsi | 93 +++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/arch/arm/boot/dts/exynos4210.dtsi b/arch/arm/boot/dts/exynos4210.dtsi index 19a8e711e61627..a9d6f4b6a44b9a 100644 --- a/arch/arm/boot/dts/exynos4210.dtsi +++ b/arch/arm/boot/dts/exynos4210.dtsi @@ -52,6 +52,99 @@ }; }; + memory_bus: memory_bus@0 { + compatible = "samsung,exynos-memory-bus"; + + operating-points = < + 400000 1150000 + 267000 1050000 + 133000 1025000>; + status = "disabled"; + + blocks { + dmc_block: memory_bus_block1 { + clocks = <&clock CLK_DIV_DMC>; + clock-names = "memory-bus"; + frequency = < + 400000 + 267000 + 133000>; + }; + + acp_block: memory_bus_block2 { + clocks = <&clock CLK_DIV_ACP>; + clock-names = "memory-bus"; + frequency = < + 200000 + 160000 + 133000>; + }; + + peri_block: memory_bus_block3 { + clocks = <&clock CLK_ACLK100>; + clock-names = "memory-bus"; + frequency = < + 100000 + 100000 + 100000>; + }; + + fsys_block: memory_bus_block4 { + clocks = <&clock CLK_ACLK133>; + clock-names = "memory-bus"; + frequency = < + 133000 + 133000 + 100000>; + }; + + display_block: memory_bus_block5 { + clocks = <&clock CLK_ACLK160>; + clock-names = "memory-bus"; + frequency = < + 160000 + 133000 + 100000>; + }; + + lcd0_block: memory_bus_block6 { + clocks = <&clock CLK_ACLK200>; + clock-names = "memory-bus"; + frequency = < + 200000 + 160000 + 100000>; + }; + + leftbus_block: memory_bus_block7 { + clocks = <&clock CLK_DIV_GDL>; + clock-names = "memory-bus"; + frequency = < + 200000 + 160000 + 100000>; + }; + + rightbus_block: memory_bus_block8 { + clocks = <&clock CLK_DIV_GDR>; + clock-names = "memory-bus"; + frequency = < + 200000 + 160000 + 100000>; + }; + + mfc_block: memory_bus_block9 { + clocks = <&clock CLK_SCLK_MFC>; + clock-names = "memory-bus"; + frequency = < + 200000 + 160000 + 100000>; + }; + }; + }; + pmu_system_controller: system-controller@10020000 { clock-names = "clkout0", "clkout1", "clkout2", "clkout3", "clkout4", "clkout8", "clkout9"; From 76dd480982ec8397dd0b21f380f3e76eed573a24 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Thu, 15 Jan 2015 10:50:55 +0900 Subject: [PATCH 713/788] ARM: dts: Add the support for exynos busfreq on Exynos3250-based Rinato/Monk board This patch adds the Exynos3250 memory-bus node which includes the regulator and devfreq-event phandle. The devfreq-event phandle is used for the governor of devfreq device and provide the current usage state of MIF (Memory Interface) / INT (Internal) memory bus group. Cc: Kukjin Kim Cc: Myungjoo Ham Cc: Kyungmin Park Cc: Youngjun Cho Signed-off-by: Chanwoo Choi Acked-by: Kyungmin Park --- arch/arm/boot/dts/exynos3250-monk.dts | 12 ++++++++++++ arch/arm/boot/dts/exynos3250-rinato.dts | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/arch/arm/boot/dts/exynos3250-monk.dts b/arch/arm/boot/dts/exynos3250-monk.dts index fcceb59a7bef84..efadb16ce4f41e 100644 --- a/arch/arm/boot/dts/exynos3250-monk.dts +++ b/arch/arm/boot/dts/exynos3250-monk.dts @@ -460,6 +460,18 @@ }; }; +&memory_bus_mif { + devfreq-events = <&ppmu_dmc0_3>, <&ppmu_dmc1_3>; + vdd-mem-supply = <&buck1_reg>; + status = "okay"; +}; + +&memory_bus_int { + devfreq-events = <&ppmu_leftbus_3>, <&ppmu_rightbus_3>; + vdd-mem-supply = <&buck3_reg>; + status = "okay"; +}; + &xusbxti { clock-frequency = <24000000>; }; diff --git a/arch/arm/boot/dts/exynos3250-rinato.dts b/arch/arm/boot/dts/exynos3250-rinato.dts index bb9865ed06851c..222fad0eeedd07 100644 --- a/arch/arm/boot/dts/exynos3250-rinato.dts +++ b/arch/arm/boot/dts/exynos3250-rinato.dts @@ -563,6 +563,18 @@ }; }; +&memory_bus_mif { + devfreq-events = <&ppmu_dmc0_3>, <&ppmu_dmc1_3>; + vdd-mem-supply = <&buck1_reg>; + status = "okay"; +}; + +&memory_bus_int { + devfreq-events = <&ppmu_leftbus_3>, <&ppmu_rightbus_3>; + vdd-mem-supply = <&buck3_reg>; + status = "okay"; +}; + &xusbxti { clock-frequency = <24000000>; }; From 06364f4c25a93d2765d07150a4ecf6c5a1a7388c Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Thu, 15 Jan 2015 10:50:56 +0900 Subject: [PATCH 714/788] ARM: dts: Add the support for exynos busfreq on Exynos4412-based TRATS2 board This patch adds the Exynos4412 memory-bus node which includes the regulator and devfreq-event phandle. The devfreq-event phandle is used for the governor of devfreq device and provide the current usage state of MIF (Memory Interface) / INT (Internal) memory bus group. Cc: Kukjin Kim Cc: Myungjoo Ham Cc: Kyungmin Park Signed-off-by: Chanwoo Choi --- arch/arm/boot/dts/exynos4412-trats2.dts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/arm/boot/dts/exynos4412-trats2.dts b/arch/arm/boot/dts/exynos4412-trats2.dts index 1f784c72e49a90..0c7284334af55d 100644 --- a/arch/arm/boot/dts/exynos4412-trats2.dts +++ b/arch/arm/boot/dts/exynos4412-trats2.dts @@ -922,6 +922,18 @@ }; }; +&memory_bus_mif { + devfreq-events = <&ppmu_dmc0_3>, <&ppmu_dmc1_3>; + vdd-mem-supply = <&buck1_reg>; + status = "okay"; +}; + +&memory_bus_int { + devfreq-events = <&ppmu_leftbus_3>, <&ppmu_rightbus_3>; + vdd-mem-supply = <&buck3_reg>; + status = "okay"; +}; + &pinctrl_0 { pinctrl-names = "default"; pinctrl-0 = <&sleep0>; From fda98ee19c402a030382c4abed522e947a689b5b Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sat, 14 Feb 2015 23:23:56 +0100 Subject: [PATCH 715/788] ARM: dts: odroid: Add support for exynos busfreq --- arch/arm/boot/dts/exynos4412-odroid-common.dtsi | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index 6f3e0c5f9224be..85ee0b799b07e8 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -547,6 +547,18 @@ }; }; +&memory_bus_mif { + devfreq-events = <&ppmu_dmc0_3>, <&ppmu_dmc1_3>; + vdd-mem-supply = <&buck1_reg>; + status = "okay"; +}; + +&memory_bus_int { + devfreq-events = <&ppmu_leftbus_3>, <&ppmu_rightbus_3>; + vdd-mem-supply = <&buck3_reg>; + status = "okay"; +}; + &pinctrl_1 { gpio_power_key: power_key { samsung,pins = "gpx1-3"; From dd8da03299f9def6440a9705de2e812e1bfda5df Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Thu, 15 Jan 2015 10:50:57 +0900 Subject: [PATCH 716/788] devfreq: exynos: Remove unused exynos4 memory busfreq driver This patch removes the unused exynos4 memory busfreq driver by adding generic exynos memory bus frequency driver. Cc: Myungjoo Ham Cc: Kyungmin Park Signed-off-by: Chanwoo Choi --- drivers/devfreq/Kconfig | 12 - drivers/devfreq/exynos/Makefile | 1 - drivers/devfreq/exynos/exynos4_bus.c | 1055 -------------------------- drivers/devfreq/exynos/exynos4_bus.h | 110 --- 4 files changed, 1178 deletions(-) delete mode 100644 drivers/devfreq/exynos/exynos4_bus.c delete mode 100644 drivers/devfreq/exynos/exynos4_bus.h diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index cb66867b0377ec..f7e5f5bbd71a20 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -80,18 +80,6 @@ config ARM_EXYNOS_BUS_DEVFREQ and adjusts the operating frequencies and voltages with OPP support. This does not yet operate with optimal voltages. -config ARM_EXYNOS4_BUS_DEVFREQ - bool "ARM Exynos4210/4212/4412 Memory Bus DEVFREQ Driver" - depends on (CPU_EXYNOS4210 || SOC_EXYNOS4212 || SOC_EXYNOS4412) && !ARCH_MULTIPLATFORM - select DEVFREQ_GOV_SIMPLE_ONDEMAND - select PM_OPP - help - This adds the DEVFREQ driver for Exynos4210 memory bus (vdd_int) - and Exynos4212/4412 memory interface and bus (vdd_mif + vdd_int). - It reads PPMU counters of memory controllers and adjusts - the operating frequencies and voltages with OPP support. - This does not yet operate with optimal voltages. - config ARM_EXYNOS5_BUS_DEVFREQ tristate "ARM Exynos5250 Bus DEVFREQ Driver" depends on SOC_EXYNOS5250 diff --git a/drivers/devfreq/exynos/Makefile b/drivers/devfreq/exynos/Makefile index 4ec06d3229961b..3f806c77a50ccc 100644 --- a/drivers/devfreq/exynos/Makefile +++ b/drivers/devfreq/exynos/Makefile @@ -1,4 +1,3 @@ # Exynos DEVFREQ Drivers obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o -obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ) += exynos_ppmu.o exynos4_bus.o obj-$(CONFIG_ARM_EXYNOS5_BUS_DEVFREQ) += exynos_ppmu.o exynos5_bus.o diff --git a/drivers/devfreq/exynos/exynos4_bus.c b/drivers/devfreq/exynos/exynos4_bus.c deleted file mode 100644 index da950920516933..00000000000000 --- a/drivers/devfreq/exynos/exynos4_bus.c +++ /dev/null @@ -1,1055 +0,0 @@ -/* drivers/devfreq/exynos4210_memorybus.c - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * MyungJoo Ham - * - * EXYNOS4 - Memory/Bus clock frequency scaling support in DEVFREQ framework - * This version supports EXYNOS4210 only. This changes bus frequencies - * and vddint voltages. Exynos4412/4212 should be able to be supported - * with minor modifications. - * - * 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. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "exynos_ppmu.h" -#include "exynos4_bus.h" - -#define MAX_SAFEVOLT 1200000 /* 1.2V */ - -enum exynos4_busf_type { - TYPE_BUSF_EXYNOS4210, - TYPE_BUSF_EXYNOS4x12, -}; - -/* Assume that the bus is saturated if the utilization is 40% */ -#define BUS_SATURATION_RATIO 40 - -enum busclk_level_idx { - LV_0 = 0, - LV_1, - LV_2, - LV_3, - LV_4, - _LV_END -}; - -enum exynos_ppmu_idx { - PPMU_DMC0, - PPMU_DMC1, - PPMU_END, -}; - -#define EX4210_LV_MAX LV_2 -#define EX4x12_LV_MAX LV_4 -#define EX4210_LV_NUM (LV_2 + 1) -#define EX4x12_LV_NUM (LV_4 + 1) - -/** - * struct busfreq_opp_info - opp information for bus - * @rate: Frequency in hertz - * @volt: Voltage in microvolts corresponding to this OPP - */ -struct busfreq_opp_info { - unsigned long rate; - unsigned long volt; -}; - -struct busfreq_data { - enum exynos4_busf_type type; - struct device *dev; - struct devfreq *devfreq; - bool disabled; - struct regulator *vdd_int; - struct regulator *vdd_mif; /* Exynos4412/4212 only */ - struct busfreq_opp_info curr_oppinfo; - struct busfreq_ppmu_data ppmu_data; - - struct notifier_block pm_notifier; - struct mutex lock; - - /* Dividers calculated at boot/probe-time */ - unsigned int dmc_divtable[_LV_END]; /* DMC0 */ - unsigned int top_divtable[_LV_END]; -}; - -/* 4210 controls clock of mif and voltage of int */ -static struct bus_opp_table exynos4210_busclk_table[] = { - {LV_0, 400000, 1150000}, - {LV_1, 267000, 1050000}, - {LV_2, 133000, 1025000}, - {0, 0, 0}, -}; - -/* - * MIF is the main control knob clock for Exynos4x12 MIF/INT - * clock and voltage of both mif/int are controlled. - */ -static struct bus_opp_table exynos4x12_mifclk_table[] = { - {LV_0, 400000, 1100000}, - {LV_1, 267000, 1000000}, - {LV_2, 160000, 950000}, - {LV_3, 133000, 950000}, - {LV_4, 100000, 950000}, - {0, 0, 0}, -}; - -/* - * INT is not the control knob of 4x12. LV_x is not meant to represent - * the current performance. (MIF does) - */ -static struct bus_opp_table exynos4x12_intclk_table[] = { - {LV_0, 200000, 1000000}, - {LV_1, 160000, 950000}, - {LV_2, 133000, 925000}, - {LV_3, 100000, 900000}, - {0, 0, 0}, -}; - -/* TODO: asv volt definitions are "__initdata"? */ -/* Some chips have different operating voltages */ -static unsigned int exynos4210_asv_volt[][EX4210_LV_NUM] = { - {1150000, 1050000, 1050000}, - {1125000, 1025000, 1025000}, - {1100000, 1000000, 1000000}, - {1075000, 975000, 975000}, - {1050000, 950000, 950000}, -}; - -static unsigned int exynos4x12_mif_step_50[][EX4x12_LV_NUM] = { - /* 400 267 160 133 100 */ - {1050000, 950000, 900000, 900000, 900000}, /* ASV0 */ - {1050000, 950000, 900000, 900000, 900000}, /* ASV1 */ - {1050000, 950000, 900000, 900000, 900000}, /* ASV2 */ - {1050000, 900000, 900000, 900000, 900000}, /* ASV3 */ - {1050000, 900000, 900000, 900000, 850000}, /* ASV4 */ - {1050000, 900000, 900000, 850000, 850000}, /* ASV5 */ - {1050000, 900000, 850000, 850000, 850000}, /* ASV6 */ - {1050000, 900000, 850000, 850000, 850000}, /* ASV7 */ - {1050000, 900000, 850000, 850000, 850000}, /* ASV8 */ -}; - -static unsigned int exynos4x12_int_volt[][EX4x12_LV_NUM] = { - /* 200 160 133 100 */ - {1000000, 950000, 925000, 900000}, /* ASV0 */ - {975000, 925000, 925000, 900000}, /* ASV1 */ - {950000, 925000, 900000, 875000}, /* ASV2 */ - {950000, 900000, 900000, 875000}, /* ASV3 */ - {925000, 875000, 875000, 875000}, /* ASV4 */ - {900000, 850000, 850000, 850000}, /* ASV5 */ - {900000, 850000, 850000, 850000}, /* ASV6 */ - {900000, 850000, 850000, 850000}, /* ASV7 */ - {900000, 850000, 850000, 850000}, /* ASV8 */ -}; - -/*** Clock Divider Data for Exynos4210 ***/ -static unsigned int exynos4210_clkdiv_dmc0[][8] = { - /* - * Clock divider value for following - * { DIVACP, DIVACP_PCLK, DIVDPHY, DIVDMC, DIVDMCD - * DIVDMCP, DIVCOPY2, DIVCORE_TIMERS } - */ - - /* DMC L0: 400MHz */ - { 3, 1, 1, 1, 1, 1, 3, 1 }, - /* DMC L1: 266.7MHz */ - { 4, 1, 1, 2, 1, 1, 3, 1 }, - /* DMC L2: 133MHz */ - { 5, 1, 1, 5, 1, 1, 3, 1 }, -}; -static unsigned int exynos4210_clkdiv_top[][5] = { - /* - * Clock divider value for following - * { DIVACLK200, DIVACLK100, DIVACLK160, DIVACLK133, DIVONENAND } - */ - /* ACLK200 L0: 200MHz */ - { 3, 7, 4, 5, 1 }, - /* ACLK200 L1: 160MHz */ - { 4, 7, 5, 6, 1 }, - /* ACLK200 L2: 133MHz */ - { 5, 7, 7, 7, 1 }, -}; -static unsigned int exynos4210_clkdiv_lr_bus[][2] = { - /* - * Clock divider value for following - * { DIVGDL/R, DIVGPL/R } - */ - /* ACLK_GDL/R L1: 200MHz */ - { 3, 1 }, - /* ACLK_GDL/R L2: 160MHz */ - { 4, 1 }, - /* ACLK_GDL/R L3: 133MHz */ - { 5, 1 }, -}; - -/*** Clock Divider Data for Exynos4212/4412 ***/ -static unsigned int exynos4x12_clkdiv_dmc0[][6] = { - /* - * Clock divider value for following - * { DIVACP, DIVACP_PCLK, DIVDPHY, DIVDMC, DIVDMCD - * DIVDMCP} - */ - - /* DMC L0: 400MHz */ - {3, 1, 1, 1, 1, 1}, - /* DMC L1: 266.7MHz */ - {4, 1, 1, 2, 1, 1}, - /* DMC L2: 160MHz */ - {5, 1, 1, 4, 1, 1}, - /* DMC L3: 133MHz */ - {5, 1, 1, 5, 1, 1}, - /* DMC L4: 100MHz */ - {7, 1, 1, 7, 1, 1}, -}; -static unsigned int exynos4x12_clkdiv_dmc1[][6] = { - /* - * Clock divider value for following - * { G2DACP, DIVC2C, DIVC2C_ACLK } - */ - - /* DMC L0: 400MHz */ - {3, 1, 1}, - /* DMC L1: 266.7MHz */ - {4, 2, 1}, - /* DMC L2: 160MHz */ - {5, 4, 1}, - /* DMC L3: 133MHz */ - {5, 5, 1}, - /* DMC L4: 100MHz */ - {7, 7, 1}, -}; -static unsigned int exynos4x12_clkdiv_top[][5] = { - /* - * Clock divider value for following - * { DIVACLK266_GPS, DIVACLK100, DIVACLK160, - DIVACLK133, DIVONENAND } - */ - - /* ACLK_GDL/R L0: 200MHz */ - {2, 7, 4, 5, 1}, - /* ACLK_GDL/R L1: 200MHz */ - {2, 7, 4, 5, 1}, - /* ACLK_GDL/R L2: 160MHz */ - {4, 7, 5, 7, 1}, - /* ACLK_GDL/R L3: 133MHz */ - {4, 7, 5, 7, 1}, - /* ACLK_GDL/R L4: 100MHz */ - {7, 7, 7, 7, 1}, -}; -static unsigned int exynos4x12_clkdiv_lr_bus[][2] = { - /* - * Clock divider value for following - * { DIVGDL/R, DIVGPL/R } - */ - - /* ACLK_GDL/R L0: 200MHz */ - {3, 1}, - /* ACLK_GDL/R L1: 200MHz */ - {3, 1}, - /* ACLK_GDL/R L2: 160MHz */ - {4, 1}, - /* ACLK_GDL/R L3: 133MHz */ - {5, 1}, - /* ACLK_GDL/R L4: 100MHz */ - {7, 1}, -}; -static unsigned int exynos4x12_clkdiv_sclkip[][3] = { - /* - * Clock divider value for following - * { DIVMFC, DIVJPEG, DIVFIMC0~3} - */ - - /* SCLK_MFC: 200MHz */ - {3, 3, 4}, - /* SCLK_MFC: 200MHz */ - {3, 3, 4}, - /* SCLK_MFC: 160MHz */ - {4, 4, 5}, - /* SCLK_MFC: 133MHz */ - {5, 5, 5}, - /* SCLK_MFC: 100MHz */ - {7, 7, 7}, -}; - - -static int exynos4210_set_busclk(struct busfreq_data *data, - struct busfreq_opp_info *oppi) -{ - unsigned int index; - unsigned int tmp; - - for (index = LV_0; index < EX4210_LV_NUM; index++) - if (oppi->rate == exynos4210_busclk_table[index].clk) - break; - - if (index == EX4210_LV_NUM) - return -EINVAL; - - /* Change Divider - DMC0 */ - tmp = data->dmc_divtable[index]; - - __raw_writel(tmp, EXYNOS4_CLKDIV_DMC0); - - do { - tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_DMC0); - } while (tmp & 0x11111111); - - /* Change Divider - TOP */ - tmp = data->top_divtable[index]; - - __raw_writel(tmp, EXYNOS4_CLKDIV_TOP); - - do { - tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_TOP); - } while (tmp & 0x11111); - - /* Change Divider - LEFTBUS */ - tmp = __raw_readl(EXYNOS4_CLKDIV_LEFTBUS); - - tmp &= ~(EXYNOS4_CLKDIV_BUS_GDLR_MASK | EXYNOS4_CLKDIV_BUS_GPLR_MASK); - - tmp |= ((exynos4210_clkdiv_lr_bus[index][0] << - EXYNOS4_CLKDIV_BUS_GDLR_SHIFT) | - (exynos4210_clkdiv_lr_bus[index][1] << - EXYNOS4_CLKDIV_BUS_GPLR_SHIFT)); - - __raw_writel(tmp, EXYNOS4_CLKDIV_LEFTBUS); - - do { - tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_LEFTBUS); - } while (tmp & 0x11); - - /* Change Divider - RIGHTBUS */ - tmp = __raw_readl(EXYNOS4_CLKDIV_RIGHTBUS); - - tmp &= ~(EXYNOS4_CLKDIV_BUS_GDLR_MASK | EXYNOS4_CLKDIV_BUS_GPLR_MASK); - - tmp |= ((exynos4210_clkdiv_lr_bus[index][0] << - EXYNOS4_CLKDIV_BUS_GDLR_SHIFT) | - (exynos4210_clkdiv_lr_bus[index][1] << - EXYNOS4_CLKDIV_BUS_GPLR_SHIFT)); - - __raw_writel(tmp, EXYNOS4_CLKDIV_RIGHTBUS); - - do { - tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_RIGHTBUS); - } while (tmp & 0x11); - - return 0; -} - -static int exynos4x12_set_busclk(struct busfreq_data *data, - struct busfreq_opp_info *oppi) -{ - unsigned int index; - unsigned int tmp; - - for (index = LV_0; index < EX4x12_LV_NUM; index++) - if (oppi->rate == exynos4x12_mifclk_table[index].clk) - break; - - if (index == EX4x12_LV_NUM) - return -EINVAL; - - /* Change Divider - DMC0 */ - tmp = data->dmc_divtable[index]; - - __raw_writel(tmp, EXYNOS4_CLKDIV_DMC0); - - do { - tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_DMC0); - } while (tmp & 0x11111111); - - /* Change Divider - DMC1 */ - tmp = __raw_readl(EXYNOS4_CLKDIV_DMC1); - - tmp &= ~(EXYNOS4_CLKDIV_DMC1_G2D_ACP_MASK | - EXYNOS4_CLKDIV_DMC1_C2C_MASK | - EXYNOS4_CLKDIV_DMC1_C2CACLK_MASK); - - tmp |= ((exynos4x12_clkdiv_dmc1[index][0] << - EXYNOS4_CLKDIV_DMC1_G2D_ACP_SHIFT) | - (exynos4x12_clkdiv_dmc1[index][1] << - EXYNOS4_CLKDIV_DMC1_C2C_SHIFT) | - (exynos4x12_clkdiv_dmc1[index][2] << - EXYNOS4_CLKDIV_DMC1_C2CACLK_SHIFT)); - - __raw_writel(tmp, EXYNOS4_CLKDIV_DMC1); - - do { - tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_DMC1); - } while (tmp & 0x111111); - - /* Change Divider - TOP */ - tmp = __raw_readl(EXYNOS4_CLKDIV_TOP); - - tmp &= ~(EXYNOS4_CLKDIV_TOP_ACLK266_GPS_MASK | - EXYNOS4_CLKDIV_TOP_ACLK100_MASK | - EXYNOS4_CLKDIV_TOP_ACLK160_MASK | - EXYNOS4_CLKDIV_TOP_ACLK133_MASK | - EXYNOS4_CLKDIV_TOP_ONENAND_MASK); - - tmp |= ((exynos4x12_clkdiv_top[index][0] << - EXYNOS4_CLKDIV_TOP_ACLK266_GPS_SHIFT) | - (exynos4x12_clkdiv_top[index][1] << - EXYNOS4_CLKDIV_TOP_ACLK100_SHIFT) | - (exynos4x12_clkdiv_top[index][2] << - EXYNOS4_CLKDIV_TOP_ACLK160_SHIFT) | - (exynos4x12_clkdiv_top[index][3] << - EXYNOS4_CLKDIV_TOP_ACLK133_SHIFT) | - (exynos4x12_clkdiv_top[index][4] << - EXYNOS4_CLKDIV_TOP_ONENAND_SHIFT)); - - __raw_writel(tmp, EXYNOS4_CLKDIV_TOP); - - do { - tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_TOP); - } while (tmp & 0x11111); - - /* Change Divider - LEFTBUS */ - tmp = __raw_readl(EXYNOS4_CLKDIV_LEFTBUS); - - tmp &= ~(EXYNOS4_CLKDIV_BUS_GDLR_MASK | EXYNOS4_CLKDIV_BUS_GPLR_MASK); - - tmp |= ((exynos4x12_clkdiv_lr_bus[index][0] << - EXYNOS4_CLKDIV_BUS_GDLR_SHIFT) | - (exynos4x12_clkdiv_lr_bus[index][1] << - EXYNOS4_CLKDIV_BUS_GPLR_SHIFT)); - - __raw_writel(tmp, EXYNOS4_CLKDIV_LEFTBUS); - - do { - tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_LEFTBUS); - } while (tmp & 0x11); - - /* Change Divider - RIGHTBUS */ - tmp = __raw_readl(EXYNOS4_CLKDIV_RIGHTBUS); - - tmp &= ~(EXYNOS4_CLKDIV_BUS_GDLR_MASK | EXYNOS4_CLKDIV_BUS_GPLR_MASK); - - tmp |= ((exynos4x12_clkdiv_lr_bus[index][0] << - EXYNOS4_CLKDIV_BUS_GDLR_SHIFT) | - (exynos4x12_clkdiv_lr_bus[index][1] << - EXYNOS4_CLKDIV_BUS_GPLR_SHIFT)); - - __raw_writel(tmp, EXYNOS4_CLKDIV_RIGHTBUS); - - do { - tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_RIGHTBUS); - } while (tmp & 0x11); - - /* Change Divider - MFC */ - tmp = __raw_readl(EXYNOS4_CLKDIV_MFC); - - tmp &= ~(EXYNOS4_CLKDIV_MFC_MASK); - - tmp |= ((exynos4x12_clkdiv_sclkip[index][0] << - EXYNOS4_CLKDIV_MFC_SHIFT)); - - __raw_writel(tmp, EXYNOS4_CLKDIV_MFC); - - do { - tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_MFC); - } while (tmp & 0x1); - - /* Change Divider - JPEG */ - tmp = __raw_readl(EXYNOS4_CLKDIV_CAM1); - - tmp &= ~(EXYNOS4_CLKDIV_CAM1_JPEG_MASK); - - tmp |= ((exynos4x12_clkdiv_sclkip[index][1] << - EXYNOS4_CLKDIV_CAM1_JPEG_SHIFT)); - - __raw_writel(tmp, EXYNOS4_CLKDIV_CAM1); - - do { - tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_CAM1); - } while (tmp & 0x1); - - /* Change Divider - FIMC0~3 */ - tmp = __raw_readl(EXYNOS4_CLKDIV_CAM); - - tmp &= ~(EXYNOS4_CLKDIV_CAM_FIMC0_MASK | EXYNOS4_CLKDIV_CAM_FIMC1_MASK | - EXYNOS4_CLKDIV_CAM_FIMC2_MASK | EXYNOS4_CLKDIV_CAM_FIMC3_MASK); - - tmp |= ((exynos4x12_clkdiv_sclkip[index][2] << - EXYNOS4_CLKDIV_CAM_FIMC0_SHIFT) | - (exynos4x12_clkdiv_sclkip[index][2] << - EXYNOS4_CLKDIV_CAM_FIMC1_SHIFT) | - (exynos4x12_clkdiv_sclkip[index][2] << - EXYNOS4_CLKDIV_CAM_FIMC2_SHIFT) | - (exynos4x12_clkdiv_sclkip[index][2] << - EXYNOS4_CLKDIV_CAM_FIMC3_SHIFT)); - - __raw_writel(tmp, EXYNOS4_CLKDIV_CAM); - - do { - tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_CAM1); - } while (tmp & 0x1111); - - return 0; -} - -static int exynos4x12_get_intspec(unsigned long mifclk) -{ - int i = 0; - - while (exynos4x12_intclk_table[i].clk) { - if (exynos4x12_intclk_table[i].clk <= mifclk) - return i; - i++; - } - - return -EINVAL; -} - -static int exynos4_bus_setvolt(struct busfreq_data *data, - struct busfreq_opp_info *oppi, - struct busfreq_opp_info *oldoppi) -{ - int err = 0, tmp; - unsigned long volt = oppi->volt; - - switch (data->type) { - case TYPE_BUSF_EXYNOS4210: - /* OPP represents DMC clock + INT voltage */ - err = regulator_set_voltage(data->vdd_int, volt, - MAX_SAFEVOLT); - break; - case TYPE_BUSF_EXYNOS4x12: - /* OPP represents MIF clock + MIF voltage */ - err = regulator_set_voltage(data->vdd_mif, volt, - MAX_SAFEVOLT); - if (err) - break; - - tmp = exynos4x12_get_intspec(oppi->rate); - if (tmp < 0) { - err = tmp; - regulator_set_voltage(data->vdd_mif, - oldoppi->volt, - MAX_SAFEVOLT); - break; - } - err = regulator_set_voltage(data->vdd_int, - exynos4x12_intclk_table[tmp].volt, - MAX_SAFEVOLT); - /* Try to recover */ - if (err) - regulator_set_voltage(data->vdd_mif, - oldoppi->volt, - MAX_SAFEVOLT); - break; - default: - err = -EINVAL; - } - - return err; -} - -static int exynos4_bus_target(struct device *dev, unsigned long *_freq, - u32 flags) -{ - int err = 0; - struct platform_device *pdev = container_of(dev, struct platform_device, - dev); - struct busfreq_data *data = platform_get_drvdata(pdev); - struct dev_pm_opp *opp; - unsigned long freq; - unsigned long old_freq = data->curr_oppinfo.rate; - struct busfreq_opp_info new_oppinfo; - - rcu_read_lock(); - opp = devfreq_recommended_opp(dev, _freq, flags); - if (IS_ERR(opp)) { - rcu_read_unlock(); - return PTR_ERR(opp); - } - new_oppinfo.rate = dev_pm_opp_get_freq(opp); - new_oppinfo.volt = dev_pm_opp_get_voltage(opp); - rcu_read_unlock(); - freq = new_oppinfo.rate; - - if (old_freq == freq) - return 0; - - dev_dbg(dev, "targeting %lukHz %luuV\n", freq, new_oppinfo.volt); - - mutex_lock(&data->lock); - - if (data->disabled) - goto out; - - if (old_freq < freq) - err = exynos4_bus_setvolt(data, &new_oppinfo, - &data->curr_oppinfo); - if (err) - goto out; - - if (old_freq != freq) { - switch (data->type) { - case TYPE_BUSF_EXYNOS4210: - err = exynos4210_set_busclk(data, &new_oppinfo); - break; - case TYPE_BUSF_EXYNOS4x12: - err = exynos4x12_set_busclk(data, &new_oppinfo); - break; - default: - err = -EINVAL; - } - } - if (err) - goto out; - - if (old_freq > freq) - err = exynos4_bus_setvolt(data, &new_oppinfo, - &data->curr_oppinfo); - if (err) - goto out; - - data->curr_oppinfo = new_oppinfo; -out: - mutex_unlock(&data->lock); - return err; -} - -static int exynos4_bus_get_dev_status(struct device *dev, - struct devfreq_dev_status *stat) -{ - struct busfreq_data *data = dev_get_drvdata(dev); - struct busfreq_ppmu_data *ppmu_data = &data->ppmu_data; - int busier; - - exynos_read_ppmu(ppmu_data); - busier = exynos_get_busier_ppmu(ppmu_data); - stat->current_frequency = data->curr_oppinfo.rate; - - /* Number of cycles spent on memory access */ - stat->busy_time = ppmu_data->ppmu[busier].count[PPMU_PMNCNT3]; - stat->busy_time *= 100 / BUS_SATURATION_RATIO; - stat->total_time = ppmu_data->ppmu[busier].ccnt; - - /* If the counters have overflown, retry */ - if (ppmu_data->ppmu[busier].ccnt_overflow || - ppmu_data->ppmu[busier].count_overflow[0]) - return -EAGAIN; - - return 0; -} - -static struct devfreq_dev_profile exynos4_devfreq_profile = { - .initial_freq = 400000, - .polling_ms = 50, - .target = exynos4_bus_target, - .get_dev_status = exynos4_bus_get_dev_status, -}; - -static int exynos4210_init_tables(struct busfreq_data *data) -{ - u32 tmp; - int mgrp; - int i, err = 0; - - tmp = __raw_readl(EXYNOS4_CLKDIV_DMC0); - for (i = LV_0; i < EX4210_LV_NUM; i++) { - tmp &= ~(EXYNOS4_CLKDIV_DMC0_ACP_MASK | - EXYNOS4_CLKDIV_DMC0_ACPPCLK_MASK | - EXYNOS4_CLKDIV_DMC0_DPHY_MASK | - EXYNOS4_CLKDIV_DMC0_DMC_MASK | - EXYNOS4_CLKDIV_DMC0_DMCD_MASK | - EXYNOS4_CLKDIV_DMC0_DMCP_MASK | - EXYNOS4_CLKDIV_DMC0_COPY2_MASK | - EXYNOS4_CLKDIV_DMC0_CORETI_MASK); - - tmp |= ((exynos4210_clkdiv_dmc0[i][0] << - EXYNOS4_CLKDIV_DMC0_ACP_SHIFT) | - (exynos4210_clkdiv_dmc0[i][1] << - EXYNOS4_CLKDIV_DMC0_ACPPCLK_SHIFT) | - (exynos4210_clkdiv_dmc0[i][2] << - EXYNOS4_CLKDIV_DMC0_DPHY_SHIFT) | - (exynos4210_clkdiv_dmc0[i][3] << - EXYNOS4_CLKDIV_DMC0_DMC_SHIFT) | - (exynos4210_clkdiv_dmc0[i][4] << - EXYNOS4_CLKDIV_DMC0_DMCD_SHIFT) | - (exynos4210_clkdiv_dmc0[i][5] << - EXYNOS4_CLKDIV_DMC0_DMCP_SHIFT) | - (exynos4210_clkdiv_dmc0[i][6] << - EXYNOS4_CLKDIV_DMC0_COPY2_SHIFT) | - (exynos4210_clkdiv_dmc0[i][7] << - EXYNOS4_CLKDIV_DMC0_CORETI_SHIFT)); - - data->dmc_divtable[i] = tmp; - } - - tmp = __raw_readl(EXYNOS4_CLKDIV_TOP); - for (i = LV_0; i < EX4210_LV_NUM; i++) { - tmp &= ~(EXYNOS4_CLKDIV_TOP_ACLK200_MASK | - EXYNOS4_CLKDIV_TOP_ACLK100_MASK | - EXYNOS4_CLKDIV_TOP_ACLK160_MASK | - EXYNOS4_CLKDIV_TOP_ACLK133_MASK | - EXYNOS4_CLKDIV_TOP_ONENAND_MASK); - - tmp |= ((exynos4210_clkdiv_top[i][0] << - EXYNOS4_CLKDIV_TOP_ACLK200_SHIFT) | - (exynos4210_clkdiv_top[i][1] << - EXYNOS4_CLKDIV_TOP_ACLK100_SHIFT) | - (exynos4210_clkdiv_top[i][2] << - EXYNOS4_CLKDIV_TOP_ACLK160_SHIFT) | - (exynos4210_clkdiv_top[i][3] << - EXYNOS4_CLKDIV_TOP_ACLK133_SHIFT) | - (exynos4210_clkdiv_top[i][4] << - EXYNOS4_CLKDIV_TOP_ONENAND_SHIFT)); - - data->top_divtable[i] = tmp; - } - - /* - * TODO: init tmp based on busfreq_data - * (device-tree or platform-data) - */ - tmp = 0; /* Max voltages for the reliability of the unknown */ - - pr_debug("ASV Group of Exynos4 is %d\n", tmp); - /* Use merged grouping for voltage */ - switch (tmp) { - case 0: - mgrp = 0; - break; - case 1: - case 2: - mgrp = 1; - break; - case 3: - case 4: - mgrp = 2; - break; - case 5: - case 6: - mgrp = 3; - break; - case 7: - mgrp = 4; - break; - default: - pr_warn("Unknown ASV Group. Use max voltage.\n"); - mgrp = 0; - } - - for (i = LV_0; i < EX4210_LV_NUM; i++) - exynos4210_busclk_table[i].volt = exynos4210_asv_volt[mgrp][i]; - - for (i = LV_0; i < EX4210_LV_NUM; i++) { - err = dev_pm_opp_add(data->dev, exynos4210_busclk_table[i].clk, - exynos4210_busclk_table[i].volt); - if (err) { - dev_err(data->dev, "Cannot add opp entries.\n"); - return err; - } - } - - - return 0; -} - -static int exynos4x12_init_tables(struct busfreq_data *data) -{ - unsigned int i; - unsigned int tmp; - int ret; - - /* Enable pause function for DREX2 DVFS */ - tmp = __raw_readl(EXYNOS4_DMC_PAUSE_CTRL); - tmp |= EXYNOS4_DMC_PAUSE_ENABLE; - __raw_writel(tmp, EXYNOS4_DMC_PAUSE_CTRL); - - tmp = __raw_readl(EXYNOS4_CLKDIV_DMC0); - - for (i = 0; i < EX4x12_LV_NUM; i++) { - tmp &= ~(EXYNOS4_CLKDIV_DMC0_ACP_MASK | - EXYNOS4_CLKDIV_DMC0_ACPPCLK_MASK | - EXYNOS4_CLKDIV_DMC0_DPHY_MASK | - EXYNOS4_CLKDIV_DMC0_DMC_MASK | - EXYNOS4_CLKDIV_DMC0_DMCD_MASK | - EXYNOS4_CLKDIV_DMC0_DMCP_MASK); - - tmp |= ((exynos4x12_clkdiv_dmc0[i][0] << - EXYNOS4_CLKDIV_DMC0_ACP_SHIFT) | - (exynos4x12_clkdiv_dmc0[i][1] << - EXYNOS4_CLKDIV_DMC0_ACPPCLK_SHIFT) | - (exynos4x12_clkdiv_dmc0[i][2] << - EXYNOS4_CLKDIV_DMC0_DPHY_SHIFT) | - (exynos4x12_clkdiv_dmc0[i][3] << - EXYNOS4_CLKDIV_DMC0_DMC_SHIFT) | - (exynos4x12_clkdiv_dmc0[i][4] << - EXYNOS4_CLKDIV_DMC0_DMCD_SHIFT) | - (exynos4x12_clkdiv_dmc0[i][5] << - EXYNOS4_CLKDIV_DMC0_DMCP_SHIFT)); - - data->dmc_divtable[i] = tmp; - } - - tmp = 0; /* Max voltages for the reliability of the unknown */ - - if (tmp > 8) - tmp = 0; - pr_debug("ASV Group of Exynos4x12 is %d\n", tmp); - - for (i = 0; i < EX4x12_LV_NUM; i++) { - exynos4x12_mifclk_table[i].volt = - exynos4x12_mif_step_50[tmp][i]; - exynos4x12_intclk_table[i].volt = - exynos4x12_int_volt[tmp][i]; - } - - for (i = 0; i < EX4x12_LV_NUM; i++) { - ret = dev_pm_opp_add(data->dev, exynos4x12_mifclk_table[i].clk, - exynos4x12_mifclk_table[i].volt); - if (ret) { - dev_err(data->dev, "Fail to add opp entries.\n"); - return ret; - } - } - - return 0; -} - -static int exynos4_busfreq_pm_notifier_event(struct notifier_block *this, - unsigned long event, void *ptr) -{ - struct busfreq_data *data = container_of(this, struct busfreq_data, - pm_notifier); - struct dev_pm_opp *opp; - struct busfreq_opp_info new_oppinfo; - unsigned long maxfreq = ULONG_MAX; - int err = 0; - - switch (event) { - case PM_SUSPEND_PREPARE: - /* Set Fastest and Deactivate DVFS */ - mutex_lock(&data->lock); - - data->disabled = true; - - rcu_read_lock(); - opp = dev_pm_opp_find_freq_floor(data->dev, &maxfreq); - if (IS_ERR(opp)) { - rcu_read_unlock(); - dev_err(data->dev, "%s: unable to find a min freq\n", - __func__); - mutex_unlock(&data->lock); - return PTR_ERR(opp); - } - new_oppinfo.rate = dev_pm_opp_get_freq(opp); - new_oppinfo.volt = dev_pm_opp_get_voltage(opp); - rcu_read_unlock(); - - err = exynos4_bus_setvolt(data, &new_oppinfo, - &data->curr_oppinfo); - if (err) - goto unlock; - - switch (data->type) { - case TYPE_BUSF_EXYNOS4210: - err = exynos4210_set_busclk(data, &new_oppinfo); - break; - case TYPE_BUSF_EXYNOS4x12: - err = exynos4x12_set_busclk(data, &new_oppinfo); - break; - default: - err = -EINVAL; - } - if (err) - goto unlock; - - data->curr_oppinfo = new_oppinfo; -unlock: - mutex_unlock(&data->lock); - if (err) - return err; - return NOTIFY_OK; - case PM_POST_RESTORE: - case PM_POST_SUSPEND: - /* Reactivate */ - mutex_lock(&data->lock); - data->disabled = false; - mutex_unlock(&data->lock); - return NOTIFY_OK; - } - - return NOTIFY_DONE; -} - -static int exynos4_busfreq_probe(struct platform_device *pdev) -{ - struct busfreq_data *data; - struct busfreq_ppmu_data *ppmu_data; - struct dev_pm_opp *opp; - struct device *dev = &pdev->dev; - int err = 0; - - data = devm_kzalloc(&pdev->dev, sizeof(struct busfreq_data), GFP_KERNEL); - if (data == NULL) { - dev_err(dev, "Cannot allocate memory.\n"); - return -ENOMEM; - } - - ppmu_data = &data->ppmu_data; - ppmu_data->ppmu_end = PPMU_END; - ppmu_data->ppmu = devm_kzalloc(dev, - sizeof(struct exynos_ppmu) * PPMU_END, - GFP_KERNEL); - if (!ppmu_data->ppmu) { - dev_err(dev, "Failed to allocate memory for exynos_ppmu\n"); - return -ENOMEM; - } - - data->type = pdev->id_entry->driver_data; - ppmu_data->ppmu[PPMU_DMC0].hw_base = S5P_VA_DMC0; - ppmu_data->ppmu[PPMU_DMC1].hw_base = S5P_VA_DMC1; - data->pm_notifier.notifier_call = exynos4_busfreq_pm_notifier_event; - data->dev = dev; - mutex_init(&data->lock); - - switch (data->type) { - case TYPE_BUSF_EXYNOS4210: - err = exynos4210_init_tables(data); - break; - case TYPE_BUSF_EXYNOS4x12: - err = exynos4x12_init_tables(data); - break; - default: - dev_err(dev, "Cannot determine the device id %d\n", data->type); - err = -EINVAL; - } - if (err) { - dev_err(dev, "Cannot initialize busfreq table %d\n", - data->type); - return err; - } - - data->vdd_int = devm_regulator_get(dev, "vdd_int"); - if (IS_ERR(data->vdd_int)) { - dev_err(dev, "Cannot get the regulator \"vdd_int\"\n"); - return PTR_ERR(data->vdd_int); - } - if (data->type == TYPE_BUSF_EXYNOS4x12) { - data->vdd_mif = devm_regulator_get(dev, "vdd_mif"); - if (IS_ERR(data->vdd_mif)) { - dev_err(dev, "Cannot get the regulator \"vdd_mif\"\n"); - return PTR_ERR(data->vdd_mif); - } - } - - rcu_read_lock(); - opp = dev_pm_opp_find_freq_floor(dev, - &exynos4_devfreq_profile.initial_freq); - if (IS_ERR(opp)) { - rcu_read_unlock(); - dev_err(dev, "Invalid initial frequency %lu kHz.\n", - exynos4_devfreq_profile.initial_freq); - return PTR_ERR(opp); - } - data->curr_oppinfo.rate = dev_pm_opp_get_freq(opp); - data->curr_oppinfo.volt = dev_pm_opp_get_voltage(opp); - rcu_read_unlock(); - - platform_set_drvdata(pdev, data); - - data->devfreq = devm_devfreq_add_device(dev, &exynos4_devfreq_profile, - "simple_ondemand", NULL); - if (IS_ERR(data->devfreq)) - return PTR_ERR(data->devfreq); - - /* - * Start PPMU (Performance Profiling Monitoring Unit) to check - * utilization of each IP in the Exynos4 SoC. - */ - busfreq_mon_reset(ppmu_data); - - /* Register opp_notifier for Exynos4 busfreq */ - err = devm_devfreq_register_opp_notifier(dev, data->devfreq); - if (err < 0) { - dev_err(dev, "Failed to register opp notifier\n"); - return err; - } - - /* Register pm_notifier for Exynos4 busfreq */ - err = register_pm_notifier(&data->pm_notifier); - if (err) { - dev_err(dev, "Failed to setup pm notifier\n"); - return err; - } - - return 0; -} - -static int exynos4_busfreq_remove(struct platform_device *pdev) -{ - struct busfreq_data *data = platform_get_drvdata(pdev); - - /* Unregister all of notifier chain */ - unregister_pm_notifier(&data->pm_notifier); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int exynos4_busfreq_resume(struct device *dev) -{ - struct busfreq_data *data = dev_get_drvdata(dev); - struct busfreq_ppmu_data *ppmu_data = &data->ppmu_data; - - busfreq_mon_reset(ppmu_data); - return 0; -} -#endif - -static SIMPLE_DEV_PM_OPS(exynos4_busfreq_pm_ops, NULL, exynos4_busfreq_resume); - -static const struct platform_device_id exynos4_busfreq_id[] = { - { "exynos4210-busfreq", TYPE_BUSF_EXYNOS4210 }, - { "exynos4412-busfreq", TYPE_BUSF_EXYNOS4x12 }, - { "exynos4212-busfreq", TYPE_BUSF_EXYNOS4x12 }, - { }, -}; - -static struct platform_driver exynos4_busfreq_driver = { - .probe = exynos4_busfreq_probe, - .remove = exynos4_busfreq_remove, - .id_table = exynos4_busfreq_id, - .driver = { - .name = "exynos4-busfreq", - .pm = &exynos4_busfreq_pm_ops, - }, -}; - -static int __init exynos4_busfreq_init(void) -{ - return platform_driver_register(&exynos4_busfreq_driver); -} -late_initcall(exynos4_busfreq_init); - -static void __exit exynos4_busfreq_exit(void) -{ - platform_driver_unregister(&exynos4_busfreq_driver); -} -module_exit(exynos4_busfreq_exit); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("EXYNOS4 busfreq driver with devfreq framework"); -MODULE_AUTHOR("MyungJoo Ham "); diff --git a/drivers/devfreq/exynos/exynos4_bus.h b/drivers/devfreq/exynos/exynos4_bus.h deleted file mode 100644 index 94c73c18d28cd0..00000000000000 --- a/drivers/devfreq/exynos/exynos4_bus.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2013 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * EXYNOS4 BUS header - * - * 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. -*/ - -#ifndef __DEVFREQ_EXYNOS4_BUS_H -#define __DEVFREQ_EXYNOS4_BUS_H __FILE__ - -#include - -#define EXYNOS4_CLKDIV_LEFTBUS (S5P_VA_CMU + 0x04500) -#define EXYNOS4_CLKDIV_STAT_LEFTBUS (S5P_VA_CMU + 0x04600) - -#define EXYNOS4_CLKDIV_RIGHTBUS (S5P_VA_CMU + 0x08500) -#define EXYNOS4_CLKDIV_STAT_RIGHTBUS (S5P_VA_CMU + 0x08600) - -#define EXYNOS4_CLKDIV_TOP (S5P_VA_CMU + 0x0C510) -#define EXYNOS4_CLKDIV_CAM (S5P_VA_CMU + 0x0C520) -#define EXYNOS4_CLKDIV_MFC (S5P_VA_CMU + 0x0C528) - -#define EXYNOS4_CLKDIV_STAT_TOP (S5P_VA_CMU + 0x0C610) -#define EXYNOS4_CLKDIV_STAT_MFC (S5P_VA_CMU + 0x0C628) - -#define EXYNOS4210_CLKGATE_IP_IMAGE (S5P_VA_CMU + 0x0C930) -#define EXYNOS4212_CLKGATE_IP_IMAGE (S5P_VA_CMU + 0x04930) - -#define EXYNOS4_CLKDIV_DMC0 (S5P_VA_CMU + 0x10500) -#define EXYNOS4_CLKDIV_DMC1 (S5P_VA_CMU + 0x10504) -#define EXYNOS4_CLKDIV_STAT_DMC0 (S5P_VA_CMU + 0x10600) -#define EXYNOS4_CLKDIV_STAT_DMC1 (S5P_VA_CMU + 0x10604) - -#define EXYNOS4_DMC_PAUSE_CTRL (S5P_VA_CMU + 0x11094) -#define EXYNOS4_DMC_PAUSE_ENABLE (1 << 0) - -#define EXYNOS4_CLKDIV_DMC0_ACP_SHIFT (0) -#define EXYNOS4_CLKDIV_DMC0_ACP_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_ACP_SHIFT) -#define EXYNOS4_CLKDIV_DMC0_ACPPCLK_SHIFT (4) -#define EXYNOS4_CLKDIV_DMC0_ACPPCLK_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_ACPPCLK_SHIFT) -#define EXYNOS4_CLKDIV_DMC0_DPHY_SHIFT (8) -#define EXYNOS4_CLKDIV_DMC0_DPHY_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_DPHY_SHIFT) -#define EXYNOS4_CLKDIV_DMC0_DMC_SHIFT (12) -#define EXYNOS4_CLKDIV_DMC0_DMC_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_DMC_SHIFT) -#define EXYNOS4_CLKDIV_DMC0_DMCD_SHIFT (16) -#define EXYNOS4_CLKDIV_DMC0_DMCD_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_DMCD_SHIFT) -#define EXYNOS4_CLKDIV_DMC0_DMCP_SHIFT (20) -#define EXYNOS4_CLKDIV_DMC0_DMCP_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_DMCP_SHIFT) -#define EXYNOS4_CLKDIV_DMC0_COPY2_SHIFT (24) -#define EXYNOS4_CLKDIV_DMC0_COPY2_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_COPY2_SHIFT) -#define EXYNOS4_CLKDIV_DMC0_CORETI_SHIFT (28) -#define EXYNOS4_CLKDIV_DMC0_CORETI_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_CORETI_SHIFT) - -#define EXYNOS4_CLKDIV_DMC1_G2D_ACP_SHIFT (0) -#define EXYNOS4_CLKDIV_DMC1_G2D_ACP_MASK (0xf << EXYNOS4_CLKDIV_DMC1_G2D_ACP_SHIFT) -#define EXYNOS4_CLKDIV_DMC1_C2C_SHIFT (4) -#define EXYNOS4_CLKDIV_DMC1_C2C_MASK (0x7 << EXYNOS4_CLKDIV_DMC1_C2C_SHIFT) -#define EXYNOS4_CLKDIV_DMC1_PWI_SHIFT (8) -#define EXYNOS4_CLKDIV_DMC1_PWI_MASK (0xf << EXYNOS4_CLKDIV_DMC1_PWI_SHIFT) -#define EXYNOS4_CLKDIV_DMC1_C2CACLK_SHIFT (12) -#define EXYNOS4_CLKDIV_DMC1_C2CACLK_MASK (0x7 << EXYNOS4_CLKDIV_DMC1_C2CACLK_SHIFT) -#define EXYNOS4_CLKDIV_DMC1_DVSEM_SHIFT (16) -#define EXYNOS4_CLKDIV_DMC1_DVSEM_MASK (0x7f << EXYNOS4_CLKDIV_DMC1_DVSEM_SHIFT) -#define EXYNOS4_CLKDIV_DMC1_DPM_SHIFT (24) -#define EXYNOS4_CLKDIV_DMC1_DPM_MASK (0x7f << EXYNOS4_CLKDIV_DMC1_DPM_SHIFT) - -#define EXYNOS4_CLKDIV_MFC_SHIFT (0) -#define EXYNOS4_CLKDIV_MFC_MASK (0x7 << EXYNOS4_CLKDIV_MFC_SHIFT) - -#define EXYNOS4_CLKDIV_TOP_ACLK200_SHIFT (0) -#define EXYNOS4_CLKDIV_TOP_ACLK200_MASK (0x7 << EXYNOS4_CLKDIV_TOP_ACLK200_SHIFT) -#define EXYNOS4_CLKDIV_TOP_ACLK100_SHIFT (4) -#define EXYNOS4_CLKDIV_TOP_ACLK100_MASK (0xF << EXYNOS4_CLKDIV_TOP_ACLK100_SHIFT) -#define EXYNOS4_CLKDIV_TOP_ACLK160_SHIFT (8) -#define EXYNOS4_CLKDIV_TOP_ACLK160_MASK (0x7 << EXYNOS4_CLKDIV_TOP_ACLK160_SHIFT) -#define EXYNOS4_CLKDIV_TOP_ACLK133_SHIFT (12) -#define EXYNOS4_CLKDIV_TOP_ACLK133_MASK (0x7 << EXYNOS4_CLKDIV_TOP_ACLK133_SHIFT) -#define EXYNOS4_CLKDIV_TOP_ONENAND_SHIFT (16) -#define EXYNOS4_CLKDIV_TOP_ONENAND_MASK (0x7 << EXYNOS4_CLKDIV_TOP_ONENAND_SHIFT) -#define EXYNOS4_CLKDIV_TOP_ACLK266_GPS_SHIFT (20) -#define EXYNOS4_CLKDIV_TOP_ACLK266_GPS_MASK (0x7 << EXYNOS4_CLKDIV_TOP_ACLK266_GPS_SHIFT) -#define EXYNOS4_CLKDIV_TOP_ACLK400_MCUISP_SHIFT (24) -#define EXYNOS4_CLKDIV_TOP_ACLK400_MCUISP_MASK (0x7 << EXYNOS4_CLKDIV_TOP_ACLK400_MCUISP_SHIFT) - -#define EXYNOS4_CLKDIV_BUS_GDLR_SHIFT (0) -#define EXYNOS4_CLKDIV_BUS_GDLR_MASK (0x7 << EXYNOS4_CLKDIV_BUS_GDLR_SHIFT) -#define EXYNOS4_CLKDIV_BUS_GPLR_SHIFT (4) -#define EXYNOS4_CLKDIV_BUS_GPLR_MASK (0x7 << EXYNOS4_CLKDIV_BUS_GPLR_SHIFT) - -#define EXYNOS4_CLKDIV_CAM_FIMC0_SHIFT (0) -#define EXYNOS4_CLKDIV_CAM_FIMC0_MASK (0xf << EXYNOS4_CLKDIV_CAM_FIMC0_SHIFT) -#define EXYNOS4_CLKDIV_CAM_FIMC1_SHIFT (4) -#define EXYNOS4_CLKDIV_CAM_FIMC1_MASK (0xf << EXYNOS4_CLKDIV_CAM_FIMC1_SHIFT) -#define EXYNOS4_CLKDIV_CAM_FIMC2_SHIFT (8) -#define EXYNOS4_CLKDIV_CAM_FIMC2_MASK (0xf << EXYNOS4_CLKDIV_CAM_FIMC2_SHIFT) -#define EXYNOS4_CLKDIV_CAM_FIMC3_SHIFT (12) -#define EXYNOS4_CLKDIV_CAM_FIMC3_MASK (0xf << EXYNOS4_CLKDIV_CAM_FIMC3_SHIFT) - -#define EXYNOS4_CLKDIV_CAM1 (S5P_VA_CMU + 0x0C568) - -#define EXYNOS4_CLKDIV_STAT_CAM1 (S5P_VA_CMU + 0x0C668) - -#define EXYNOS4_CLKDIV_CAM1_JPEG_SHIFT (0) -#define EXYNOS4_CLKDIV_CAM1_JPEG_MASK (0xf << EXYNOS4_CLKDIV_CAM1_JPEG_SHIFT) - -#endif /* __DEVFREQ_EXYNOS4_BUS_H */ From ad6d5447962c48dd71dc3528dd425587ef3336dc Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sat, 14 Feb 2015 23:21:13 +0100 Subject: [PATCH 717/788] patchset: [PATCH v4 0/9] devfreq: Add generic exynos memory-bus frequency driver --- PATCHES | 1 + 1 file changed, 1 insertion(+) diff --git a/PATCHES b/PATCHES index 88397e66727d3f..753fc2bf2d292e 100644 --- a/PATCHES +++ b/PATCHES @@ -7,3 +7,4 @@ [PATCH v4 0/2] serial: samsung: add support for early console [RFC v2 0/7] HDMI CEC framework [PATCH v3 0/8] hwmon: thermal: Odroid U3: Provide support for Odroid U3 fan +[PATCH v4 0/9] devfreq: Add generic exynos memory-bus frequency driver From a15b11670c1a54b1e4b56d8b3f5c470c06f80b42 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sat, 15 Feb 2014 21:26:08 +0100 Subject: [PATCH 718/788] mali: initial import of r4p0 kernel driver --- drivers/gpu/Makefile | 2 +- drivers/gpu/arm/Kconfig | 3 + drivers/gpu/arm/Makefile | 1 + drivers/gpu/arm/mali/Kbuild | 212 ++ drivers/gpu/arm/mali/Kconfig | 81 + drivers/gpu/arm/mali/Makefile | 159 ++ drivers/gpu/arm/mali/common/mali_broadcast.c | 124 + drivers/gpu/arm/mali/common/mali_broadcast.h | 52 + drivers/gpu/arm/mali/common/mali_dlbu.c | 209 ++ drivers/gpu/arm/mali/common/mali_dlbu.h | 46 + drivers/gpu/arm/mali/common/mali_dma.c | 201 ++ drivers/gpu/arm/mali/common/mali_dma.h | 190 ++ drivers/gpu/arm/mali/common/mali_gp.c | 337 +++ drivers/gpu/arm/mali/common/mali_gp.h | 93 + drivers/gpu/arm/mali/common/mali_gp_job.c | 131 ++ drivers/gpu/arm/mali/common/mali_gp_job.h | 186 ++ .../gpu/arm/mali/common/mali_gp_scheduler.c | 701 ++++++ .../gpu/arm/mali/common/mali_gp_scheduler.h | 101 + drivers/gpu/arm/mali/common/mali_group.c | 1855 +++++++++++++++ drivers/gpu/arm/mali/common/mali_group.h | 309 +++ drivers/gpu/arm/mali/common/mali_hw_core.c | 45 + drivers/gpu/arm/mali/common/mali_hw_core.h | 100 + .../gpu/arm/mali/common/mali_kernel_common.h | 175 ++ .../gpu/arm/mali/common/mali_kernel_core.c | 1399 +++++++++++ .../gpu/arm/mali/common/mali_kernel_core.h | 56 + .../common/mali_kernel_descriptor_mapping.c | 173 ++ .../common/mali_kernel_descriptor_mapping.h | 99 + .../arm/mali/common/mali_kernel_utilization.c | 439 ++++ .../arm/mali/common/mali_kernel_utilization.h | 65 + .../gpu/arm/mali/common/mali_kernel_vsync.c | 49 + drivers/gpu/arm/mali/common/mali_l2_cache.c | 581 +++++ drivers/gpu/arm/mali/common/mali_l2_cache.h | 89 + .../gpu/arm/mali/common/mali_mem_validation.c | 65 + .../gpu/arm/mali/common/mali_mem_validation.h | 19 + drivers/gpu/arm/mali/common/mali_mmu.c | 430 ++++ drivers/gpu/arm/mali/common/mali_mmu.h | 114 + .../arm/mali/common/mali_mmu_page_directory.c | 436 ++++ .../arm/mali/common/mali_mmu_page_directory.h | 107 + drivers/gpu/arm/mali/common/mali_osk.h | 1335 +++++++++++ drivers/gpu/arm/mali/common/mali_osk_bitops.h | 162 ++ drivers/gpu/arm/mali/common/mali_osk_list.h | 273 +++ drivers/gpu/arm/mali/common/mali_osk_mali.h | 118 + .../gpu/arm/mali/common/mali_osk_profiling.h | 141 ++ drivers/gpu/arm/mali/common/mali_osk_types.h | 455 ++++ drivers/gpu/arm/mali/common/mali_pm.c | 122 + drivers/gpu/arm/mali/common/mali_pm.h | 28 + drivers/gpu/arm/mali/common/mali_pm_domain.c | 241 ++ drivers/gpu/arm/mali/common/mali_pm_domain.h | 74 + drivers/gpu/arm/mali/common/mali_pmu.c | 406 ++++ drivers/gpu/arm/mali/common/mali_pmu.h | 134 ++ drivers/gpu/arm/mali/common/mali_pp.c | 573 +++++ drivers/gpu/arm/mali/common/mali_pp.h | 131 ++ drivers/gpu/arm/mali/common/mali_pp_job.c | 278 +++ drivers/gpu/arm/mali/common/mali_pp_job.h | 384 +++ .../gpu/arm/mali/common/mali_pp_scheduler.c | 2067 +++++++++++++++++ .../gpu/arm/mali/common/mali_pp_scheduler.h | 130 ++ drivers/gpu/arm/mali/common/mali_scheduler.c | 112 + drivers/gpu/arm/mali/common/mali_scheduler.h | 90 + .../arm/mali/common/mali_scheduler_types.h | 34 + drivers/gpu/arm/mali/common/mali_session.c | 81 + drivers/gpu/arm/mali/common/mali_session.h | 94 + drivers/gpu/arm/mali/common/mali_soft_job.c | 464 ++++ drivers/gpu/arm/mali/common/mali_soft_job.h | 196 ++ .../arm/mali/common/mali_spinlock_reentrant.c | 77 + .../arm/mali/common/mali_spinlock_reentrant.h | 70 + drivers/gpu/arm/mali/common/mali_timeline.c | 1374 +++++++++++ drivers/gpu/arm/mali/common/mali_timeline.h | 494 ++++ .../mali/common/mali_timeline_fence_wait.c | 198 ++ .../mali/common/mali_timeline_fence_wait.h | 67 + .../mali/common/mali_timeline_sync_fence.c | 158 ++ .../mali/common/mali_timeline_sync_fence.h | 51 + drivers/gpu/arm/mali/common/mali_ukk.h | 614 +++++ .../arm/mali/common/mali_user_settings_db.c | 146 ++ .../arm/mali/common/mali_user_settings_db.h | 39 + .../arm/mali/include/linux/mali/mali_utgard.h | 418 ++++ .../include/linux/mali/mali_utgard_counters.h | 261 +++ .../include/linux/mali/mali_utgard_ioctl.h | 95 + .../linux/mali/mali_utgard_profiling_events.h | 174 ++ .../mali/mali_utgard_profiling_gator_api.h | 197 ++ .../include/linux/mali/mali_utgard_uk_types.h | 1132 +++++++++ .../linux/license/gpl/mali_kernel_license.h | 30 + .../arm/mali/linux/mali_device_pause_resume.c | 38 + .../gpu/arm/mali/linux/mali_kernel_linux.c | 804 +++++++ .../gpu/arm/mali/linux/mali_kernel_linux.h | 33 + .../gpu/arm/mali/linux/mali_kernel_sysfs.c | 1390 +++++++++++ .../gpu/arm/mali/linux/mali_kernel_sysfs.h | 29 + drivers/gpu/arm/mali/linux/mali_linux_trace.h | 126 + drivers/gpu/arm/mali/linux/mali_memory.c | 353 +++ drivers/gpu/arm/mali/linux/mali_memory.h | 134 ++ .../arm/mali/linux/mali_memory_block_alloc.c | 319 +++ .../arm/mali/linux/mali_memory_block_alloc.h | 29 + .../gpu/arm/mali/linux/mali_memory_dma_buf.c | 434 ++++ .../gpu/arm/mali/linux/mali_memory_dma_buf.h | 40 + .../gpu/arm/mali/linux/mali_memory_external.c | 127 + .../gpu/arm/mali/linux/mali_memory_os_alloc.c | 556 +++++ .../gpu/arm/mali/linux/mali_memory_os_alloc.h | 47 + .../gpu/arm/mali/linux/mali_memory_types.h | 100 + drivers/gpu/arm/mali/linux/mali_memory_ump.c | 215 ++ drivers/gpu/arm/mali/linux/mali_osk_atomics.c | 60 + drivers/gpu/arm/mali/linux/mali_osk_irq.c | 204 ++ drivers/gpu/arm/mali/linux/mali_osk_locks.c | 281 +++ drivers/gpu/arm/mali/linux/mali_osk_locks.h | 326 +++ .../arm/mali/linux/mali_osk_low_level_mem.c | 137 ++ drivers/gpu/arm/mali/linux/mali_osk_mali.c | 127 + drivers/gpu/arm/mali/linux/mali_osk_math.c | 27 + drivers/gpu/arm/mali/linux/mali_osk_memory.c | 61 + drivers/gpu/arm/mali/linux/mali_osk_misc.c | 72 + .../arm/mali/linux/mali_osk_notification.c | 182 ++ drivers/gpu/arm/mali/linux/mali_osk_pm.c | 109 + .../gpu/arm/mali/linux/mali_osk_profiling.c | 308 +++ .../gpu/arm/mali/linux/mali_osk_specific.h | 92 + drivers/gpu/arm/mali/linux/mali_osk_time.c | 51 + drivers/gpu/arm/mali/linux/mali_osk_timers.c | 76 + .../gpu/arm/mali/linux/mali_osk_wait_queue.c | 78 + drivers/gpu/arm/mali/linux/mali_osk_wq.c | 231 ++ .../arm/mali/linux/mali_pmu_power_up_down.c | 71 + .../arm/mali/linux/mali_profiling_events.h | 17 + .../arm/mali/linux/mali_profiling_gator_api.h | 17 + .../arm/mali/linux/mali_profiling_internal.c | 274 +++ .../arm/mali/linux/mali_profiling_internal.h | 35 + drivers/gpu/arm/mali/linux/mali_sync.c | 304 +++ drivers/gpu/arm/mali/linux/mali_sync.h | 112 + drivers/gpu/arm/mali/linux/mali_uk_types.h | 17 + drivers/gpu/arm/mali/linux/mali_ukk_core.c | 113 + drivers/gpu/arm/mali/linux/mali_ukk_gp.c | 91 + drivers/gpu/arm/mali/linux/mali_ukk_mem.c | 237 ++ drivers/gpu/arm/mali/linux/mali_ukk_pp.c | 105 + .../gpu/arm/mali/linux/mali_ukk_profiling.c | 168 ++ .../gpu/arm/mali/linux/mali_ukk_soft_job.c | 86 + .../gpu/arm/mali/linux/mali_ukk_timeline.c | 88 + drivers/gpu/arm/mali/linux/mali_ukk_vsync.c | 39 + .../gpu/arm/mali/linux/mali_ukk_wrappers.h | 75 + drivers/gpu/arm/mali/platform/arm/arm.c | 228 ++ .../arm/mali/platform/arm/arm_core_scaling.c | 122 + .../arm/mali/platform/arm/arm_core_scaling.h | 44 + drivers/gpu/arm/mali/readme.txt | 24 + drivers/gpu/arm/mali/regs/mali_200_regs.h | 130 ++ drivers/gpu/arm/mali/regs/mali_gp_regs.h | 172 ++ .../mali/timestamp-arm11-cc/mali_timestamp.c | 13 + .../mali/timestamp-arm11-cc/mali_timestamp.h | 48 + .../mali/timestamp-default/mali_timestamp.c | 13 + .../mali/timestamp-default/mali_timestamp.h | 26 + drivers/gpu/arm/ump/Kbuild | 94 + drivers/gpu/arm/ump/Kconfig | 16 + drivers/gpu/arm/ump/Makefile | 67 + drivers/gpu/arm/ump/Makefile.common | 20 + drivers/gpu/arm/ump/arch-pb-virtex5/config.h | 18 + drivers/gpu/arm/ump/common/ump_kernel_api.c | 492 ++++ .../gpu/arm/ump/common/ump_kernel_common.c | 370 +++ .../gpu/arm/ump/common/ump_kernel_common.h | 125 + .../common/ump_kernel_descriptor_mapping.c | 155 ++ .../common/ump_kernel_descriptor_mapping.h | 89 + .../ump/common/ump_kernel_memory_backend.h | 48 + .../gpu/arm/ump/common/ump_kernel_ref_drv.c | 186 ++ drivers/gpu/arm/ump/common/ump_kernel_types.h | 50 + drivers/gpu/arm/ump/common/ump_osk.h | 48 + drivers/gpu/arm/ump/common/ump_uk_types.h | 193 ++ drivers/gpu/arm/ump/common/ump_ukk.h | 60 + .../linux/license/gpl/ump_kernel_license.h | 30 + drivers/gpu/arm/ump/linux/ump_ioctl.h | 53 + drivers/gpu/arm/ump/linux/ump_kernel_linux.c | 447 ++++ drivers/gpu/arm/ump/linux/ump_kernel_linux.h | 18 + .../ump_kernel_memory_backend_dedicated.c | 271 +++ .../ump_kernel_memory_backend_dedicated.h | 23 + .../ump/linux/ump_kernel_memory_backend_os.c | 235 ++ .../ump/linux/ump_kernel_memory_backend_os.h | 23 + .../gpu/arm/ump/linux/ump_memory_backend.c | 65 + drivers/gpu/arm/ump/linux/ump_osk_atomics.c | 27 + .../gpu/arm/ump/linux/ump_osk_low_level_mem.c | 314 +++ drivers/gpu/arm/ump/linux/ump_osk_misc.c | 36 + .../gpu/arm/ump/linux/ump_ukk_ref_wrappers.c | 71 + .../gpu/arm/ump/linux/ump_ukk_ref_wrappers.h | 34 + drivers/gpu/arm/ump/linux/ump_ukk_wrappers.c | 280 +++ drivers/gpu/arm/ump/linux/ump_ukk_wrappers.h | 46 + drivers/gpu/arm/ump/readme.txt | 28 + drivers/gpu/arm/umplock/Makefile | 69 + drivers/gpu/arm/umplock/umplock_driver.c | 598 +++++ drivers/gpu/arm/umplock/umplock_ioctl.h | 66 + drivers/gpu/drm/Kconfig | 6 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/mali/Makefile | 19 + drivers/gpu/drm/mali/mali_drv.c | 166 ++ drivers/gpu/drm/mali/mali_drv.h | 39 + drivers/gpu/drm/mali/mali_mm.c | 262 +++ include/uapi/drm/Kbuild | 1 + include/uapi/drm/mali_drm.h | 44 + include/ump/ump_kernel_interface.h | 235 ++ include/ump/ump_kernel_interface_ref_drv.h | 31 + include/ump/ump_kernel_platform.h | 35 + 189 files changed, 38620 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/arm/Kconfig create mode 100644 drivers/gpu/arm/Makefile create mode 100644 drivers/gpu/arm/mali/Kbuild create mode 100644 drivers/gpu/arm/mali/Kconfig create mode 100644 drivers/gpu/arm/mali/Makefile create mode 100644 drivers/gpu/arm/mali/common/mali_broadcast.c create mode 100644 drivers/gpu/arm/mali/common/mali_broadcast.h create mode 100644 drivers/gpu/arm/mali/common/mali_dlbu.c create mode 100644 drivers/gpu/arm/mali/common/mali_dlbu.h create mode 100644 drivers/gpu/arm/mali/common/mali_dma.c create mode 100644 drivers/gpu/arm/mali/common/mali_dma.h create mode 100644 drivers/gpu/arm/mali/common/mali_gp.c create mode 100644 drivers/gpu/arm/mali/common/mali_gp.h create mode 100644 drivers/gpu/arm/mali/common/mali_gp_job.c create mode 100644 drivers/gpu/arm/mali/common/mali_gp_job.h create mode 100644 drivers/gpu/arm/mali/common/mali_gp_scheduler.c create mode 100644 drivers/gpu/arm/mali/common/mali_gp_scheduler.h create mode 100644 drivers/gpu/arm/mali/common/mali_group.c create mode 100644 drivers/gpu/arm/mali/common/mali_group.h create mode 100644 drivers/gpu/arm/mali/common/mali_hw_core.c create mode 100644 drivers/gpu/arm/mali/common/mali_hw_core.h create mode 100644 drivers/gpu/arm/mali/common/mali_kernel_common.h create mode 100644 drivers/gpu/arm/mali/common/mali_kernel_core.c create mode 100644 drivers/gpu/arm/mali/common/mali_kernel_core.h create mode 100644 drivers/gpu/arm/mali/common/mali_kernel_descriptor_mapping.c create mode 100644 drivers/gpu/arm/mali/common/mali_kernel_descriptor_mapping.h create mode 100644 drivers/gpu/arm/mali/common/mali_kernel_utilization.c create mode 100644 drivers/gpu/arm/mali/common/mali_kernel_utilization.h create mode 100644 drivers/gpu/arm/mali/common/mali_kernel_vsync.c create mode 100644 drivers/gpu/arm/mali/common/mali_l2_cache.c create mode 100644 drivers/gpu/arm/mali/common/mali_l2_cache.h create mode 100644 drivers/gpu/arm/mali/common/mali_mem_validation.c create mode 100644 drivers/gpu/arm/mali/common/mali_mem_validation.h create mode 100644 drivers/gpu/arm/mali/common/mali_mmu.c create mode 100644 drivers/gpu/arm/mali/common/mali_mmu.h create mode 100644 drivers/gpu/arm/mali/common/mali_mmu_page_directory.c create mode 100644 drivers/gpu/arm/mali/common/mali_mmu_page_directory.h create mode 100644 drivers/gpu/arm/mali/common/mali_osk.h create mode 100644 drivers/gpu/arm/mali/common/mali_osk_bitops.h create mode 100644 drivers/gpu/arm/mali/common/mali_osk_list.h create mode 100644 drivers/gpu/arm/mali/common/mali_osk_mali.h create mode 100644 drivers/gpu/arm/mali/common/mali_osk_profiling.h create mode 100644 drivers/gpu/arm/mali/common/mali_osk_types.h create mode 100644 drivers/gpu/arm/mali/common/mali_pm.c create mode 100644 drivers/gpu/arm/mali/common/mali_pm.h create mode 100644 drivers/gpu/arm/mali/common/mali_pm_domain.c create mode 100644 drivers/gpu/arm/mali/common/mali_pm_domain.h create mode 100644 drivers/gpu/arm/mali/common/mali_pmu.c create mode 100644 drivers/gpu/arm/mali/common/mali_pmu.h create mode 100644 drivers/gpu/arm/mali/common/mali_pp.c create mode 100644 drivers/gpu/arm/mali/common/mali_pp.h create mode 100644 drivers/gpu/arm/mali/common/mali_pp_job.c create mode 100644 drivers/gpu/arm/mali/common/mali_pp_job.h create mode 100644 drivers/gpu/arm/mali/common/mali_pp_scheduler.c create mode 100644 drivers/gpu/arm/mali/common/mali_pp_scheduler.h create mode 100644 drivers/gpu/arm/mali/common/mali_scheduler.c create mode 100644 drivers/gpu/arm/mali/common/mali_scheduler.h create mode 100644 drivers/gpu/arm/mali/common/mali_scheduler_types.h create mode 100644 drivers/gpu/arm/mali/common/mali_session.c create mode 100644 drivers/gpu/arm/mali/common/mali_session.h create mode 100644 drivers/gpu/arm/mali/common/mali_soft_job.c create mode 100644 drivers/gpu/arm/mali/common/mali_soft_job.h create mode 100644 drivers/gpu/arm/mali/common/mali_spinlock_reentrant.c create mode 100644 drivers/gpu/arm/mali/common/mali_spinlock_reentrant.h create mode 100644 drivers/gpu/arm/mali/common/mali_timeline.c create mode 100644 drivers/gpu/arm/mali/common/mali_timeline.h create mode 100644 drivers/gpu/arm/mali/common/mali_timeline_fence_wait.c create mode 100644 drivers/gpu/arm/mali/common/mali_timeline_fence_wait.h create mode 100644 drivers/gpu/arm/mali/common/mali_timeline_sync_fence.c create mode 100644 drivers/gpu/arm/mali/common/mali_timeline_sync_fence.h create mode 100644 drivers/gpu/arm/mali/common/mali_ukk.h create mode 100644 drivers/gpu/arm/mali/common/mali_user_settings_db.c create mode 100644 drivers/gpu/arm/mali/common/mali_user_settings_db.h create mode 100644 drivers/gpu/arm/mali/include/linux/mali/mali_utgard.h create mode 100644 drivers/gpu/arm/mali/include/linux/mali/mali_utgard_counters.h create mode 100644 drivers/gpu/arm/mali/include/linux/mali/mali_utgard_ioctl.h create mode 100644 drivers/gpu/arm/mali/include/linux/mali/mali_utgard_profiling_events.h create mode 100644 drivers/gpu/arm/mali/include/linux/mali/mali_utgard_profiling_gator_api.h create mode 100644 drivers/gpu/arm/mali/include/linux/mali/mali_utgard_uk_types.h create mode 100644 drivers/gpu/arm/mali/linux/license/gpl/mali_kernel_license.h create mode 100644 drivers/gpu/arm/mali/linux/mali_device_pause_resume.c create mode 100644 drivers/gpu/arm/mali/linux/mali_kernel_linux.c create mode 100644 drivers/gpu/arm/mali/linux/mali_kernel_linux.h create mode 100644 drivers/gpu/arm/mali/linux/mali_kernel_sysfs.c create mode 100644 drivers/gpu/arm/mali/linux/mali_kernel_sysfs.h create mode 100644 drivers/gpu/arm/mali/linux/mali_linux_trace.h create mode 100644 drivers/gpu/arm/mali/linux/mali_memory.c create mode 100644 drivers/gpu/arm/mali/linux/mali_memory.h create mode 100644 drivers/gpu/arm/mali/linux/mali_memory_block_alloc.c create mode 100644 drivers/gpu/arm/mali/linux/mali_memory_block_alloc.h create mode 100644 drivers/gpu/arm/mali/linux/mali_memory_dma_buf.c create mode 100644 drivers/gpu/arm/mali/linux/mali_memory_dma_buf.h create mode 100644 drivers/gpu/arm/mali/linux/mali_memory_external.c create mode 100644 drivers/gpu/arm/mali/linux/mali_memory_os_alloc.c create mode 100644 drivers/gpu/arm/mali/linux/mali_memory_os_alloc.h create mode 100644 drivers/gpu/arm/mali/linux/mali_memory_types.h create mode 100644 drivers/gpu/arm/mali/linux/mali_memory_ump.c create mode 100644 drivers/gpu/arm/mali/linux/mali_osk_atomics.c create mode 100644 drivers/gpu/arm/mali/linux/mali_osk_irq.c create mode 100644 drivers/gpu/arm/mali/linux/mali_osk_locks.c create mode 100644 drivers/gpu/arm/mali/linux/mali_osk_locks.h create mode 100644 drivers/gpu/arm/mali/linux/mali_osk_low_level_mem.c create mode 100644 drivers/gpu/arm/mali/linux/mali_osk_mali.c create mode 100644 drivers/gpu/arm/mali/linux/mali_osk_math.c create mode 100644 drivers/gpu/arm/mali/linux/mali_osk_memory.c create mode 100644 drivers/gpu/arm/mali/linux/mali_osk_misc.c create mode 100644 drivers/gpu/arm/mali/linux/mali_osk_notification.c create mode 100644 drivers/gpu/arm/mali/linux/mali_osk_pm.c create mode 100644 drivers/gpu/arm/mali/linux/mali_osk_profiling.c create mode 100644 drivers/gpu/arm/mali/linux/mali_osk_specific.h create mode 100644 drivers/gpu/arm/mali/linux/mali_osk_time.c create mode 100644 drivers/gpu/arm/mali/linux/mali_osk_timers.c create mode 100644 drivers/gpu/arm/mali/linux/mali_osk_wait_queue.c create mode 100644 drivers/gpu/arm/mali/linux/mali_osk_wq.c create mode 100644 drivers/gpu/arm/mali/linux/mali_pmu_power_up_down.c create mode 100644 drivers/gpu/arm/mali/linux/mali_profiling_events.h create mode 100644 drivers/gpu/arm/mali/linux/mali_profiling_gator_api.h create mode 100644 drivers/gpu/arm/mali/linux/mali_profiling_internal.c create mode 100644 drivers/gpu/arm/mali/linux/mali_profiling_internal.h create mode 100644 drivers/gpu/arm/mali/linux/mali_sync.c create mode 100644 drivers/gpu/arm/mali/linux/mali_sync.h create mode 100644 drivers/gpu/arm/mali/linux/mali_uk_types.h create mode 100644 drivers/gpu/arm/mali/linux/mali_ukk_core.c create mode 100644 drivers/gpu/arm/mali/linux/mali_ukk_gp.c create mode 100644 drivers/gpu/arm/mali/linux/mali_ukk_mem.c create mode 100644 drivers/gpu/arm/mali/linux/mali_ukk_pp.c create mode 100644 drivers/gpu/arm/mali/linux/mali_ukk_profiling.c create mode 100644 drivers/gpu/arm/mali/linux/mali_ukk_soft_job.c create mode 100644 drivers/gpu/arm/mali/linux/mali_ukk_timeline.c create mode 100644 drivers/gpu/arm/mali/linux/mali_ukk_vsync.c create mode 100644 drivers/gpu/arm/mali/linux/mali_ukk_wrappers.h create mode 100644 drivers/gpu/arm/mali/platform/arm/arm.c create mode 100644 drivers/gpu/arm/mali/platform/arm/arm_core_scaling.c create mode 100644 drivers/gpu/arm/mali/platform/arm/arm_core_scaling.h create mode 100644 drivers/gpu/arm/mali/readme.txt create mode 100644 drivers/gpu/arm/mali/regs/mali_200_regs.h create mode 100644 drivers/gpu/arm/mali/regs/mali_gp_regs.h create mode 100644 drivers/gpu/arm/mali/timestamp-arm11-cc/mali_timestamp.c create mode 100644 drivers/gpu/arm/mali/timestamp-arm11-cc/mali_timestamp.h create mode 100644 drivers/gpu/arm/mali/timestamp-default/mali_timestamp.c create mode 100644 drivers/gpu/arm/mali/timestamp-default/mali_timestamp.h create mode 100644 drivers/gpu/arm/ump/Kbuild create mode 100644 drivers/gpu/arm/ump/Kconfig create mode 100644 drivers/gpu/arm/ump/Makefile create mode 100644 drivers/gpu/arm/ump/Makefile.common create mode 100644 drivers/gpu/arm/ump/arch-pb-virtex5/config.h create mode 100644 drivers/gpu/arm/ump/common/ump_kernel_api.c create mode 100644 drivers/gpu/arm/ump/common/ump_kernel_common.c create mode 100644 drivers/gpu/arm/ump/common/ump_kernel_common.h create mode 100644 drivers/gpu/arm/ump/common/ump_kernel_descriptor_mapping.c create mode 100644 drivers/gpu/arm/ump/common/ump_kernel_descriptor_mapping.h create mode 100644 drivers/gpu/arm/ump/common/ump_kernel_memory_backend.h create mode 100644 drivers/gpu/arm/ump/common/ump_kernel_ref_drv.c create mode 100644 drivers/gpu/arm/ump/common/ump_kernel_types.h create mode 100644 drivers/gpu/arm/ump/common/ump_osk.h create mode 100644 drivers/gpu/arm/ump/common/ump_uk_types.h create mode 100644 drivers/gpu/arm/ump/common/ump_ukk.h create mode 100644 drivers/gpu/arm/ump/linux/license/gpl/ump_kernel_license.h create mode 100644 drivers/gpu/arm/ump/linux/ump_ioctl.h create mode 100644 drivers/gpu/arm/ump/linux/ump_kernel_linux.c create mode 100644 drivers/gpu/arm/ump/linux/ump_kernel_linux.h create mode 100644 drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_dedicated.c create mode 100644 drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_dedicated.h create mode 100644 drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_os.c create mode 100644 drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_os.h create mode 100644 drivers/gpu/arm/ump/linux/ump_memory_backend.c create mode 100644 drivers/gpu/arm/ump/linux/ump_osk_atomics.c create mode 100644 drivers/gpu/arm/ump/linux/ump_osk_low_level_mem.c create mode 100644 drivers/gpu/arm/ump/linux/ump_osk_misc.c create mode 100644 drivers/gpu/arm/ump/linux/ump_ukk_ref_wrappers.c create mode 100644 drivers/gpu/arm/ump/linux/ump_ukk_ref_wrappers.h create mode 100644 drivers/gpu/arm/ump/linux/ump_ukk_wrappers.c create mode 100644 drivers/gpu/arm/ump/linux/ump_ukk_wrappers.h create mode 100644 drivers/gpu/arm/ump/readme.txt create mode 100644 drivers/gpu/arm/umplock/Makefile create mode 100644 drivers/gpu/arm/umplock/umplock_driver.c create mode 100644 drivers/gpu/arm/umplock/umplock_ioctl.h create mode 100644 drivers/gpu/drm/mali/Makefile create mode 100644 drivers/gpu/drm/mali/mali_drv.c create mode 100644 drivers/gpu/drm/mali/mali_drv.h create mode 100644 drivers/gpu/drm/mali/mali_mm.c create mode 100644 include/uapi/drm/mali_drm.h create mode 100644 include/ump/ump_kernel_interface.h create mode 100644 include/ump/ump_kernel_interface_ref_drv.h create mode 100644 include/ump/ump_kernel_platform.h diff --git a/drivers/gpu/Makefile b/drivers/gpu/Makefile index 70da9eb52a42cb..b6641febc830b8 100644 --- a/drivers/gpu/Makefile +++ b/drivers/gpu/Makefile @@ -1,3 +1,3 @@ -obj-y += drm/ vga/ +obj-y += drm/ vga/ arm/ obj-$(CONFIG_TEGRA_HOST1X) += host1x/ obj-$(CONFIG_IMX_IPUV3_CORE) += ipu-v3/ diff --git a/drivers/gpu/arm/Kconfig b/drivers/gpu/arm/Kconfig new file mode 100644 index 00000000000000..738741b976e234 --- /dev/null +++ b/drivers/gpu/arm/Kconfig @@ -0,0 +1,3 @@ +source "drivers/gpu/arm/mali/Kconfig" +source "drivers/gpu/arm/ump/Kconfig" +source "drivers/gpu/arm/umplock/Kconfig" diff --git a/drivers/gpu/arm/Makefile b/drivers/gpu/arm/Makefile new file mode 100644 index 00000000000000..29e74a91834152 --- /dev/null +++ b/drivers/gpu/arm/Makefile @@ -0,0 +1 @@ +obj-y += mali/ ump/ umplock/ diff --git a/drivers/gpu/arm/mali/Kbuild b/drivers/gpu/arm/mali/Kbuild new file mode 100644 index 00000000000000..6b2b4305684164 --- /dev/null +++ b/drivers/gpu/arm/mali/Kbuild @@ -0,0 +1,212 @@ +# +# Copyright (C) 2010-2011 ARM Limited. All rights reserved. +# +# This program is free software and is provided to you under the terms of the GNU General Public License version 2 +# as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. +# +# A copy of the licence is included with the program, and can also be obtained from Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# + +# This file is called by the Linux build system. + +# set up defaults if not defined by the user +TIMESTAMP ?= default +OS_MEMORY_KERNEL_BUFFER_SIZE_IN_MB ?= 16 +USING_GPU_UTILIZATION ?= 1 +PROFILING_SKIP_PP_JOBS ?= 0 +PROFILING_SKIP_PP_AND_GP_JOBS ?= 0 +MALI_PP_SCHEDULER_FORCE_NO_JOB_OVERLAP ?= 0 +MALI_PP_SCHEDULER_KEEP_SUB_JOB_STARTS_ALIGNED ?= 0 +MALI_PP_SCHEDULER_FORCE_NO_JOB_OVERLAP_BETWEEN_APPS ?= 0 +MALI_UPPER_HALF_SCHEDULING ?= 1 +MALI_ENABLE_CPU_CYCLES ?= 0 + +# For customer releases the Linux Device Drivers will be provided as ARM proprietary and GPL releases: +# The ARM proprietary product will only include the license/proprietary directory +# The GPL product will only include the license/gpl directory +ifeq ($(wildcard $(src)/linux/license/gpl/*),) + ccflags-y += -I$(src)/linux/license/proprietary + ifeq ($(CONFIG_MALI400_PROFILING),y) + $(error Profiling is incompatible with non-GPL license) + endif + ifeq ($(CONFIG_PM_RUNTIME),y) + $(error Runtime PM is incompatible with non-GPL license) + endif + ifeq ($(CONFIG_DMA_SHARED_BUFFER),y) + $(error DMA-BUF is incompatible with non-GPL license) + endif + $(error Linux Device integration is incompatible with non-GPL license) +else + ccflags-y += -I$(src)/linux/license/gpl +endif + +mali-y += \ + linux/mali_osk_atomics.o \ + linux/mali_osk_irq.o \ + linux/mali_osk_wq.o \ + linux/mali_osk_locks.o \ + linux/mali_osk_wait_queue.o \ + linux/mali_osk_low_level_mem.o \ + linux/mali_osk_math.o \ + linux/mali_osk_memory.o \ + linux/mali_osk_misc.o \ + linux/mali_osk_mali.o \ + linux/mali_osk_notification.o \ + linux/mali_osk_time.o \ + linux/mali_osk_timers.o + +mali-y += linux/mali_memory.o linux/mali_memory_os_alloc.o +mali-y += linux/mali_memory_external.o +mali-y += linux/mali_memory_block_alloc.o + +mali-y += \ + linux/mali_ukk_mem.o \ + linux/mali_ukk_gp.o \ + linux/mali_ukk_pp.o \ + linux/mali_ukk_core.o \ + linux/mali_ukk_soft_job.o \ + linux/mali_ukk_timeline.o + +# Source files which always are included in a build +mali-y += \ + common/mali_kernel_core.o \ + linux/mali_kernel_linux.o \ + common/mali_kernel_descriptor_mapping.o \ + common/mali_session.o \ + linux/mali_device_pause_resume.o \ + common/mali_kernel_vsync.o \ + linux/mali_ukk_vsync.o \ + linux/mali_kernel_sysfs.o \ + common/mali_mmu.o \ + common/mali_mmu_page_directory.o \ + common/mali_mem_validation.o \ + common/mali_hw_core.o \ + common/mali_gp.o \ + common/mali_pp.o \ + common/mali_pp_job.o \ + common/mali_gp_job.o \ + common/mali_soft_job.o \ + common/mali_scheduler.o \ + common/mali_gp_scheduler.o \ + common/mali_pp_scheduler.o \ + common/mali_group.o \ + common/mali_dlbu.o \ + common/mali_broadcast.o \ + common/mali_pm.o \ + common/mali_pmu.o \ + common/mali_user_settings_db.o \ + common/mali_kernel_utilization.o \ + common/mali_l2_cache.o \ + common/mali_dma.o \ + common/mali_timeline.o \ + common/mali_timeline_fence_wait.o \ + common/mali_timeline_sync_fence.o \ + common/mali_spinlock_reentrant.o \ + common/mali_pm_domain.o \ + linux/mali_osk_pm.o \ + linux/mali_pmu_power_up_down.o \ + __malidrv_build_info.o + +ifneq ($(MALI_PLATFORM_FILES),) + mali-y += $(MALI_PLATFORM_FILES:.c=.o) +endif + +mali-$(CONFIG_MALI400_PROFILING) += linux/mali_ukk_profiling.o +mali-$(CONFIG_MALI400_PROFILING) += linux/mali_osk_profiling.o + +mali-$(CONFIG_MALI400_INTERNAL_PROFILING) += linux/mali_profiling_internal.o timestamp-$(TIMESTAMP)/mali_timestamp.o +ccflags-$(CONFIG_MALI400_INTERNAL_PROFILING) += -I$(src)/timestamp-$(TIMESTAMP) + +mali-$(CONFIG_DMA_SHARED_BUFFER) += linux/mali_memory_dma_buf.o +mali-$(CONFIG_SYNC) += linux/mali_sync.o + +mali-$(CONFIG_MALI400_UMP) += linux/mali_memory_ump.o + +mali-$(CONFIG_MALI400_POWER_PERFORMANCE_POLICY) += common/mali_power_performance_policy.o + +# Tell the Linux build system from which .o file to create the kernel module +obj-$(CONFIG_MALI400) := mali.o + +ccflags-y += $(EXTRA_DEFINES) + +# Set up our defines, which will be passed to gcc +ccflags-y += -DPROFILING_SKIP_PP_JOBS=$(PROFILING_SKIP_PP_JOBS) +ccflags-y += -DPROFILING_SKIP_PP_AND_GP_JOBS=$(PROFILING_SKIP_PP_AND_GP_JOBS) + +ccflags-y += -DMALI_PP_SCHEDULER_FORCE_NO_JOB_OVERLAP=$(MALI_PP_SCHEDULER_FORCE_NO_JOB_OVERLAP) +ccflags-y += -DMALI_PP_SCHEDULER_KEEP_SUB_JOB_STARTS_ALIGNED=$(MALI_PP_SCHEDULER_KEEP_SUB_JOB_STARTS_ALIGNED) +ccflags-y += -DMALI_PP_SCHEDULER_FORCE_NO_JOB_OVERLAP_BETWEEN_APPS=$(MALI_PP_SCHEDULER_FORCE_NO_JOB_OVERLAP_BETWEEN_APPS) +ccflags-y += -DMALI_STATE_TRACKING=1 +ccflags-y += -DMALI_OS_MEMORY_KERNEL_BUFFER_SIZE_IN_MB=$(OS_MEMORY_KERNEL_BUFFER_SIZE_IN_MB) +ccflags-y += -DUSING_GPU_UTILIZATION=$(USING_GPU_UTILIZATION) +ccflags-y += -DMALI_ENABLE_CPU_CYCLES=$(MALI_ENABLE_CPU_CYCLES) + +ifeq ($(MALI_UPPER_HALF_SCHEDULING),1) + ccflags-y += -DMALI_UPPER_HALF_SCHEDULING +endif + +ccflags-$(CONFIG_MALI400_UMP) += -I$(src)/../../ump/include/ump +ccflags-$(CONFIG_MALI400_DEBUG) += -DDEBUG + +# Use our defines when compiling +ccflags-y += -I$(src) -I$(src)/include -I$(src)/common -I$(src)/linux -I$(src)/platform + +# Get subversion revision number, fall back to only ${MALI_RELEASE_NAME} if no svn info is available +MALI_RELEASE_NAME=$(shell cat $(src)/.version 2> /dev/null) + +SVN_INFO = (cd $(src); svn info 2>/dev/null) + +ifneq ($(shell $(SVN_INFO) 2>/dev/null),) +# SVN detected +SVN_REV := $(shell $(SVN_INFO) | grep '^Revision: '| sed -e 's/^Revision: //' 2>/dev/null) +DRIVER_REV := $(MALI_RELEASE_NAME)-r$(SVN_REV) +CHANGE_DATE := $(shell $(SVN_INFO) | grep '^Last Changed Date: ' | cut -d: -f2- | cut -b2-) +CHANGED_REVISION := $(shell $(SVN_INFO) | grep '^Last Changed Rev: ' | cut -d: -f2- | cut -b2-) +REPO_URL := $(shell $(SVN_INFO) | grep '^URL: ' | cut -d: -f2- | cut -b2-) + +else # SVN +GIT_REV := $(shell cd $(src); git describe --always 2>/dev/null) +ifneq ($(GIT_REV),) +# Git detected +DRIVER_REV := $(MALI_RELEASE_NAME)-$(GIT_REV) +CHANGE_DATE := $(shell cd $(src); git log -1 --format="%ci") +CHANGED_REVISION := $(GIT_REV) +REPO_URL := $(shell cd $(src); git describe --all --always 2>/dev/null) + +else # Git +# No Git or SVN detected +DRIVER_REV := $(MALI_RELEASE_NAME) +CHANGE_DATE := $(MALI_RELEASE_NAME) +CHANGED_REVISION := $(MALI_RELEASE_NAME) +endif +endif + +ccflags-y += -DSVN_REV_STRING=\"$(DRIVER_REV)\" + +VERSION_STRINGS := +VERSION_STRINGS += API_VERSION=$(shell cd $(src); grep "\#define _MALI_API_VERSION" $(FILES_PREFIX)include/linux/mali/mali_utgard_uk_types.h | cut -d' ' -f 3 ) +VERSION_STRINGS += REPO_URL=$(REPO_URL) +VERSION_STRINGS += REVISION=$(DRIVER_REV) +VERSION_STRINGS += CHANGED_REVISION=$(CHANGED_REVISION) +VERSION_STRINGS += CHANGE_DATE=$(CHANGE_DATE) +VERSION_STRINGS += BUILD_DATE=$(shell date) +ifdef CONFIG_MALI400_DEBUG +VERSION_STRINGS += BUILD=debug +else +VERSION_STRINGS += BUILD=release +endif +VERSION_STRINGS += TARGET_PLATFORM=$(TARGET_PLATFORM) +VERSION_STRINGS += MALI_PLATFORM=$(MALI_PLATFORM) +VERSION_STRINGS += KDIR=$(KDIR) +VERSION_STRINGS += OS_MEMORY_KERNEL_BUFFER_SIZE_IN_MB=$(OS_MEMORY_KERNEL_BUFFER_SIZE_IN_MB) +VERSION_STRINGS += USING_UMP=$(CONFIG_MALI400_UMP) +VERSION_STRINGS += USING_PROFILING=$(CONFIG_MALI400_PROFILING) +VERSION_STRINGS += USING_INTERNAL_PROFILING=$(CONFIG_MALI400_INTERNAL_PROFILING) +VERSION_STRINGS += USING_GPU_UTILIZATION=$(USING_GPU_UTILIZATION) +VERSION_STRINGS += USING_POWER_PERFORMANCE_POLICY=$(CONFIG_POWER_PERFORMANCE_POLICY) +VERSION_STRINGS += MALI_UPPER_HALF_SCHEDULING=$(MALI_UPPER_HALF_SCHEDULING) + +# Create file with Mali driver configuration +$(src)/__malidrv_build_info.c: + @echo 'const char *__malidrv_build_info(void) { return "malidrv: $(VERSION_STRINGS)";}' > $(src)/__malidrv_build_info.c diff --git a/drivers/gpu/arm/mali/Kconfig b/drivers/gpu/arm/mali/Kconfig new file mode 100644 index 00000000000000..477e6b6a943de9 --- /dev/null +++ b/drivers/gpu/arm/mali/Kconfig @@ -0,0 +1,81 @@ +config MALI400 + tristate "Mali-300/400/450 support" + depends on ARM + select DMA_SHARED_BUFFER + ---help--- + This enables support for the ARM Mali-300, Mali-400, and Mali-450 + GPUs. + + To compile this driver as a module, choose M here: the module will be + called mali. + +config MALI450 + bool "Enable Mali-450 support" + depends on MALI400 + ---help--- + This enables support for Mali-450 specific features. + +config MALI400_DEBUG + bool "Enable debug in Mali driver" + depends on MALI400 + ---help--- + This enabled extra debug checks and messages in the Mali driver. + +config MALI400_PROFILING + bool "Enable Mali profiling" + depends on MALI400 + select TRACEPOINTS + default y + ---help--- + This enables gator profiling of Mali GPU events. + +config MALI400_INTERNAL_PROFILING + bool "Enable internal Mali profiling API" + depends on MALI400_PROFILING + default n + ---help--- + This enables the internal legacy Mali profiling API. + +config MALI400_UMP + bool "Enable UMP support" + depends on MALI400 + ---help--- + This enables support for the UMP memory sharing API in the Mali driver. + +config MALI400_POWER_PERFORMANCE_POLICY + bool "Enable Mali power performance policy" + depends on ARM + default n + ---help--- + This enables support for dynamic performance scaling of Mali with the goal of lowering power consumption. + +config MALI_DMA_BUF_MAP_ON_ATTACH + bool "Map dma-buf attachments on attach" + depends on MALI400 && DMA_SHARED_BUFFER + default y + ---help--- + This makes the Mali driver map dma-buf attachments after doing + attach. If this is not set the dma-buf attachments will be mapped for + every time the GPU need to access the buffer. + + Mapping for each access can cause lower performance. + +config MALI_SHARED_INTERRUPTS + bool "Support for shared interrupts" + depends on MALI400 + default n + ---help--- + Adds functionality required to properly support shared interrupts. Without this support, + the device driver will fail during insmod if it detects shared interrupts. This also + works when the GPU is not using shared interrupts, but might have a slight performance + impact. + +config MALI_PMU_PARALLEL_POWER_UP + bool "Power up Mali PMU domains in parallel" + depends on MALI400 + default n + ---help--- + This makes the Mali driver power up all PMU power domains in parallel, instead of + powering up domains one by one, with a slight delay in between. Powering on all power + domains at the same time may cause peak currents higher than what some systems can handle. + These systems must not enable this option. diff --git a/drivers/gpu/arm/mali/Makefile b/drivers/gpu/arm/mali/Makefile new file mode 100644 index 00000000000000..c1b00913436902 --- /dev/null +++ b/drivers/gpu/arm/mali/Makefile @@ -0,0 +1,159 @@ +# +# Copyright (C) 2010-2013 ARM Limited. All rights reserved. +# +# This program is free software and is provided to you under the terms of the GNU General Public License version 2 +# as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. +# +# A copy of the licence is included with the program, and can also be obtained from Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# + +USE_UMPV2=0 +USING_PROFILING ?= 1 +USING_INTERNAL_PROFILING ?= 0 +USING_POWER_PERFORMANCE_POLICY ?= 0 +MALI_HEATMAPS_ENABLED ?= 0 +MALI_DMA_BUF_MAP_ON_ATTACH ?= 1 +MALI_PMU_PARALLEL_POWER_UP ?= 0 + +# The Makefile sets up "arch" based on the CONFIG, creates the version info +# string and the __malidrv_build_info.c file, and then call the Linux build +# system to actually build the driver. After that point the Kbuild file takes +# over. + +# set up defaults if not defined by the user +ARCH ?= arm + +OSKOS=linux +FILES_PREFIX= + +check_cc2 = \ + $(shell if $(1) -S -o /dev/null -xc /dev/null > /dev/null 2>&1; \ + then \ + echo "$(2)"; \ + else \ + echo "$(3)"; \ + fi ;) + +# This conditional makefile exports the global definition ARM_INTERNAL_BUILD. Customer releases will not include arm_internal.mak +-include ../../../arm_internal.mak + +# Give warning of old config parameters are used +ifneq ($(CONFIG),) +$(warning "You have specified the CONFIG variable which is no longer in used. Use TARGET_PLATFORM instead.") +endif + +ifneq ($(CPU),) +$(warning "You have specified the CPU variable which is no longer in used. Use TARGET_PLATFORM instead.") +endif + +# Include the mapping between TARGET_PLATFORM and KDIR + MALI_PLATFORM +-include MALI_CONFIGURATION +export KDIR ?= $(KDIR-$(TARGET_PLATFORM)) +export MALI_PLATFORM ?= $(MALI_PLATFORM-$(TARGET_PLATFORM)) + +ifneq ($(TARGET_PLATFORM),) +ifeq ($(MALI_PLATFORM),) +$(error "Invalid TARGET_PLATFORM: $(TARGET_PLATFORM)") +endif +endif + +# validate lookup result +ifeq ($(KDIR),) +$(error No KDIR found for platform $(TARGET_PLATFORM)) +endif + + +ifeq ($(USING_UMP),1) +export CONFIG_MALI400_UMP=y +export EXTRA_DEFINES += -DCONFIG_MALI400_UMP=1 +ifeq ($(USE_UMPV2),1) +UMP_SYMVERS_FILE ?= ../umpv2/Module.symvers +else +UMP_SYMVERS_FILE ?= ../ump/Module.symvers +endif +KBUILD_EXTRA_SYMBOLS = $(realpath $(UMP_SYMVERS_FILE)) +$(warning $(KBUILD_EXTRA_SYMBOLS)) +endif + +# Define host system directory +KDIR-$(shell uname -m):=/lib/modules/$(shell uname -r)/build + +include $(KDIR)/.config + +ifeq ($(ARCH), arm) +# when compiling for ARM we're cross compiling +export CROSS_COMPILE ?= $(call check_cc2, arm-linux-gnueabi-gcc, arm-linux-gnueabi-, arm-none-linux-gnueabi-) +endif + +# report detected/selected settings +ifdef ARM_INTERNAL_BUILD +$(warning TARGET_PLATFORM $(TARGET_PLATFORM)) +$(warning KDIR $(KDIR)) +$(warning MALI_PLATFORM $(MALI_PLATFORM)) +endif + +# Set up build config +export CONFIG_MALI400=m +export CONFIG_MALI450=y + +export EXTRA_DEFINES += -DCONFIG_MALI400=1 +export EXTRA_DEFINES += -DCONFIG_MALI450=1 + +ifneq ($(MALI_PLATFORM),) +export EXTRA_DEFINES += -DMALI_FAKE_PLATFORM_DEVICE=1 +export MALI_PLATFORM_FILES = $(wildcard platform/$(MALI_PLATFORM)/*.c) +endif + +ifeq ($(USING_PROFILING),1) +ifeq ($(CONFIG_TRACEPOINTS),) +$(warning CONFIG_TRACEPOINTS required for profiling) +else +export CONFIG_MALI400_PROFILING=y +export EXTRA_DEFINES += -DCONFIG_MALI400_PROFILING=1 +ifeq ($(USING_INTERNAL_PROFILING),1) +export CONFIG_MALI400_INTERNAL_PROFILING=y +export EXTRA_DEFINES += -DCONFIG_MALI400_INTERNAL_PROFILING=1 +endif +ifeq ($(MALI_HEATMAPS_ENABLED),1) +export MALI_HEATMAPS_ENABLED=y +export EXTRA_DEFINES += -DCONFIG_MALI400_HEATMAPS_ENABLED +endif +endif +endif + +ifeq ($(MALI_DMA_BUF_MAP_ON_ATTACH),1) +export CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH=y +export EXTRA_DEFINES += -DCONFIG_MALI_DMA_BUF_MAP_ON_ATTACH +endif + +ifeq ($(MALI_SHARED_INTERRUPTS),1) +export CONFIG_MALI_SHARED_INTERRUPTS=y +export EXTRA_DEFINES += -DCONFIG_MALI_SHARED_INTERRUPTS +endif + +ifeq ($(USING_POWER_PERFORMANCE_POLICY),1) +export CONFIG_MALI400_POWER_PERFORMANCE_POLICY=y +export EXTRA_DEFINES += -DCONFIG_MALI400_POWER_PERFORMANCE_POLICY +endif + +ifeq ($(MALI_PMU_PARALLEL_POWER_UP),1) +export CONFIG_MALI_PMU_PARALLEL_POWER_UP=y +export EXTRA_DEFINES += -DCONFIG_MALI_PMU_PARALLEL_POWER_UP +endif + +ifneq ($(BUILD),release) +export CONFIG_MALI400_DEBUG=y +endif + +all: $(UMP_SYMVERS_FILE) + $(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) modules + @rm $(FILES_PREFIX)__malidrv_build_info.c $(FILES_PREFIX)__malidrv_build_info.o + +clean: + $(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) clean + +kernelrelease: + $(MAKE) ARCH=$(ARCH) -C $(KDIR) kernelrelease + +export CONFIG KBUILD_EXTRA_SYMBOLS diff --git a/drivers/gpu/arm/mali/common/mali_broadcast.c b/drivers/gpu/arm/mali/common/mali_broadcast.c new file mode 100644 index 00000000000000..83cb86953bf632 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_broadcast.c @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_broadcast.h" +#include "mali_kernel_common.h" +#include "mali_osk.h" + +static const int bcast_unit_reg_size = 0x1000; +static const int bcast_unit_addr_broadcast_mask = 0x0; +static const int bcast_unit_addr_irq_override_mask = 0x4; + +struct mali_bcast_unit { + struct mali_hw_core hw_core; + u32 current_mask; +}; + +struct mali_bcast_unit *mali_bcast_unit_create(const _mali_osk_resource_t *resource) +{ + struct mali_bcast_unit *bcast_unit = NULL; + + MALI_DEBUG_ASSERT_POINTER(resource); + MALI_DEBUG_PRINT(2, ("Mali Broadcast unit: Creating Mali Broadcast unit: %s\n", resource->description)); + + bcast_unit = _mali_osk_malloc(sizeof(struct mali_bcast_unit)); + if (NULL == bcast_unit) { + MALI_PRINT_ERROR(("Mali Broadcast unit: Failed to allocate memory for Broadcast unit\n")); + return NULL; + } + + if (_MALI_OSK_ERR_OK == mali_hw_core_create(&bcast_unit->hw_core, resource, bcast_unit_reg_size)) { + bcast_unit->current_mask = 0; + mali_bcast_reset(bcast_unit); + + return bcast_unit; + } else { + MALI_PRINT_ERROR(("Mali Broadcast unit: Failed map broadcast unit\n")); + } + + _mali_osk_free(bcast_unit); + + return NULL; +} + +void mali_bcast_unit_delete(struct mali_bcast_unit *bcast_unit) +{ + MALI_DEBUG_ASSERT_POINTER(bcast_unit); + + mali_hw_core_delete(&bcast_unit->hw_core); + _mali_osk_free(bcast_unit); +} + +void mali_bcast_add_group(struct mali_bcast_unit *bcast_unit, struct mali_group *group) +{ + u32 bcast_id; + u32 broadcast_mask; + + MALI_DEBUG_ASSERT_POINTER(bcast_unit); + MALI_DEBUG_ASSERT_POINTER(group); + + bcast_id = mali_pp_core_get_bcast_id(mali_group_get_pp_core(group)); + + broadcast_mask = bcast_unit->current_mask; + + broadcast_mask |= (bcast_id); /* add PP core to broadcast */ + broadcast_mask |= (bcast_id << 16); /* add MMU to broadcast */ + + /* store mask so we can restore on reset */ + bcast_unit->current_mask = broadcast_mask; +} + +void mali_bcast_remove_group(struct mali_bcast_unit *bcast_unit, struct mali_group *group) +{ + u32 bcast_id; + u32 broadcast_mask; + + MALI_DEBUG_ASSERT_POINTER(bcast_unit); + MALI_DEBUG_ASSERT_POINTER(group); + + bcast_id = mali_pp_core_get_bcast_id(mali_group_get_pp_core(group)); + + broadcast_mask = bcast_unit->current_mask; + + broadcast_mask &= ~((bcast_id << 16) | bcast_id); + + /* store mask so we can restore on reset */ + bcast_unit->current_mask = broadcast_mask; +} + +void mali_bcast_reset(struct mali_bcast_unit *bcast_unit) +{ + MALI_DEBUG_ASSERT_POINTER(bcast_unit); + + /* set broadcast mask */ + mali_hw_core_register_write(&bcast_unit->hw_core, + bcast_unit_addr_broadcast_mask, + bcast_unit->current_mask); + + /* set IRQ override mask */ + mali_hw_core_register_write(&bcast_unit->hw_core, + bcast_unit_addr_irq_override_mask, + bcast_unit->current_mask & 0xFF); +} + +void mali_bcast_disable(struct mali_bcast_unit *bcast_unit) +{ + MALI_DEBUG_ASSERT_POINTER(bcast_unit); + + /* set broadcast mask */ + mali_hw_core_register_write(&bcast_unit->hw_core, + bcast_unit_addr_broadcast_mask, + 0x0); + + /* set IRQ override mask */ + mali_hw_core_register_write(&bcast_unit->hw_core, + bcast_unit_addr_irq_override_mask, + 0x0); +} diff --git a/drivers/gpu/arm/mali/common/mali_broadcast.h b/drivers/gpu/arm/mali/common/mali_broadcast.h new file mode 100644 index 00000000000000..95bc9bb4572a72 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_broadcast.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* + * Interface for the broadcast unit on Mali-450. + * + * - Represents up to 8 × (MMU + PP) pairs. + * - Supports dynamically changing which (MMU + PP) pairs receive the broadcast by + * setting a mask. + */ + +#include "mali_hw_core.h" +#include "mali_group.h" + +struct mali_bcast_unit; + +struct mali_bcast_unit *mali_bcast_unit_create(const _mali_osk_resource_t *resource); +void mali_bcast_unit_delete(struct mali_bcast_unit *bcast_unit); + +/* Add a group to the list of (MMU + PP) pairs broadcasts go out to. */ +void mali_bcast_add_group(struct mali_bcast_unit *bcast_unit, struct mali_group *group); + +/* Remove a group to the list of (MMU + PP) pairs broadcasts go out to. */ +void mali_bcast_remove_group(struct mali_bcast_unit *bcast_unit, struct mali_group *group); + +/* Re-set cached mask. This needs to be called after having been suspended. */ +void mali_bcast_reset(struct mali_bcast_unit *bcast_unit); + +/** + * Disable broadcast unit + * + * mali_bcast_enable must be called to re-enable the unit. Cores may not be + * added or removed when the unit is disabled. + */ +void mali_bcast_disable(struct mali_bcast_unit *bcast_unit); + +/** + * Re-enable broadcast unit + * + * This resets the masks to include the cores present when mali_bcast_disable was called. + */ +MALI_STATIC_INLINE void mali_bcast_enable(struct mali_bcast_unit *bcast_unit) +{ + mali_bcast_reset(bcast_unit); +} diff --git a/drivers/gpu/arm/mali/common/mali_dlbu.c b/drivers/gpu/arm/mali/common/mali_dlbu.c new file mode 100644 index 00000000000000..0e41d93da33739 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_dlbu.c @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_dlbu.h" +#include "mali_memory.h" +#include "mali_pp.h" +#include "mali_group.h" +#include "mali_osk.h" +#include "mali_hw_core.h" + +/** + * Size of DLBU registers in bytes + */ +#define MALI_DLBU_SIZE 0x400 + +u32 mali_dlbu_phys_addr = 0; +static mali_io_address mali_dlbu_cpu_addr = 0; + +/** + * DLBU register numbers + * Used in the register read/write routines. + * See the hardware documentation for more information about each register + */ +typedef enum mali_dlbu_register { + MALI_DLBU_REGISTER_MASTER_TLLIST_PHYS_ADDR = 0x0000, /**< Master tile list physical base address; + 31:12 Physical address to the page used for the DLBU + 0 DLBU enable - set this bit to 1 enables the AXI bus + between PPs and L2s, setting to 0 disables the router and + no further transactions are sent to DLBU */ + MALI_DLBU_REGISTER_MASTER_TLLIST_VADDR = 0x0004, /**< Master tile list virtual base address; + 31:12 Virtual address to the page used for the DLBU */ + MALI_DLBU_REGISTER_TLLIST_VBASEADDR = 0x0008, /**< Tile list virtual base address; + 31:12 Virtual address to the tile list. This address is used when + calculating the call address sent to PP.*/ + MALI_DLBU_REGISTER_FB_DIM = 0x000C, /**< Framebuffer dimension; + 23:16 Number of tiles in Y direction-1 + 7:0 Number of tiles in X direction-1 */ + MALI_DLBU_REGISTER_TLLIST_CONF = 0x0010, /**< Tile list configuration; + 29:28 select the size of each allocated block: 0=128 bytes, 1=256, 2=512, 3=1024 + 21:16 2^n number of tiles to be binned to one tile list in Y direction + 5:0 2^n number of tiles to be binned to one tile list in X direction */ + MALI_DLBU_REGISTER_START_TILE_POS = 0x0014, /**< Start tile positions; + 31:24 start position in Y direction for group 1 + 23:16 start position in X direction for group 1 + 15:8 start position in Y direction for group 0 + 7:0 start position in X direction for group 0 */ + MALI_DLBU_REGISTER_PP_ENABLE_MASK = 0x0018, /**< PP enable mask; + 7 enable PP7 for load balancing + 6 enable PP6 for load balancing + 5 enable PP5 for load balancing + 4 enable PP4 for load balancing + 3 enable PP3 for load balancing + 2 enable PP2 for load balancing + 1 enable PP1 for load balancing + 0 enable PP0 for load balancing */ +} mali_dlbu_register; + +typedef enum { + PP0ENABLE = 0, + PP1ENABLE, + PP2ENABLE, + PP3ENABLE, + PP4ENABLE, + PP5ENABLE, + PP6ENABLE, + PP7ENABLE +} mali_dlbu_pp_enable; + +struct mali_dlbu_core { + struct mali_hw_core hw_core; /**< Common for all HW cores */ + u32 pp_cores_mask; /**< This is a mask for the PP cores whose operation will be controlled by LBU + see MALI_DLBU_REGISTER_PP_ENABLE_MASK register */ +}; + +_mali_osk_errcode_t mali_dlbu_initialize(void) +{ + + MALI_DEBUG_PRINT(2, ("Mali DLBU: Initializing\n")); + + if (_MALI_OSK_ERR_OK == mali_mmu_get_table_page(&mali_dlbu_phys_addr, &mali_dlbu_cpu_addr)) { + MALI_SUCCESS; + } + + return _MALI_OSK_ERR_FAULT; +} + +void mali_dlbu_terminate(void) +{ + MALI_DEBUG_PRINT(3, ("Mali DLBU: terminating\n")); + + mali_mmu_release_table_page(mali_dlbu_phys_addr, mali_dlbu_cpu_addr); +} + +struct mali_dlbu_core *mali_dlbu_create(const _mali_osk_resource_t * resource) +{ + struct mali_dlbu_core *core = NULL; + + MALI_DEBUG_PRINT(2, ("Mali DLBU: Creating Mali dynamic load balancing unit: %s\n", resource->description)); + + core = _mali_osk_malloc(sizeof(struct mali_dlbu_core)); + if (NULL != core) { + if (_MALI_OSK_ERR_OK == mali_hw_core_create(&core->hw_core, resource, MALI_DLBU_SIZE)) { + core->pp_cores_mask = 0; + if (_MALI_OSK_ERR_OK == mali_dlbu_reset(core)) { + return core; + } + MALI_PRINT_ERROR(("Failed to reset DLBU %s\n", core->hw_core.description)); + mali_hw_core_delete(&core->hw_core); + } + + _mali_osk_free(core); + } else { + MALI_PRINT_ERROR(("Mali DLBU: Failed to allocate memory for DLBU core\n")); + } + + return NULL; +} + +void mali_dlbu_delete(struct mali_dlbu_core *dlbu) +{ + MALI_DEBUG_ASSERT_POINTER(dlbu); + + mali_dlbu_reset(dlbu); + mali_hw_core_delete(&dlbu->hw_core); + _mali_osk_free(dlbu); +} + +_mali_osk_errcode_t mali_dlbu_reset(struct mali_dlbu_core *dlbu) +{ + u32 dlbu_registers[7]; + _mali_osk_errcode_t err = _MALI_OSK_ERR_FAULT; + MALI_DEBUG_ASSERT_POINTER(dlbu); + + MALI_DEBUG_PRINT(4, ("Mali DLBU: mali_dlbu_reset: %s\n", dlbu->hw_core.description)); + + dlbu_registers[0] = mali_dlbu_phys_addr | 1; /* bit 0 enables the whole core */ + dlbu_registers[1] = MALI_DLBU_VIRT_ADDR; + dlbu_registers[2] = 0; + dlbu_registers[3] = 0; + dlbu_registers[4] = 0; + dlbu_registers[5] = 0; + dlbu_registers[6] = dlbu->pp_cores_mask; + + /* write reset values to core registers */ + mali_hw_core_register_write_array_relaxed(&dlbu->hw_core, MALI_DLBU_REGISTER_MASTER_TLLIST_PHYS_ADDR, dlbu_registers, 7); + + err = _MALI_OSK_ERR_OK; + + return err; +} + +void mali_dlbu_update_mask(struct mali_dlbu_core *dlbu) +{ + MALI_DEBUG_ASSERT_POINTER(dlbu); + + mali_hw_core_register_write(&dlbu->hw_core, MALI_DLBU_REGISTER_PP_ENABLE_MASK, dlbu->pp_cores_mask); +} + +void mali_dlbu_add_group(struct mali_dlbu_core *dlbu, struct mali_group *group) +{ + struct mali_pp_core *pp_core; + u32 bcast_id; + + MALI_DEBUG_ASSERT_POINTER( dlbu ); + MALI_DEBUG_ASSERT_POINTER( group ); + + pp_core = mali_group_get_pp_core(group); + bcast_id = mali_pp_core_get_bcast_id(pp_core); + + dlbu->pp_cores_mask |= bcast_id; + MALI_DEBUG_PRINT(3, ("Mali DLBU: Adding core[%d] New mask= 0x%02x\n", bcast_id , dlbu->pp_cores_mask)); +} + +/* Remove a group from the DLBU */ +void mali_dlbu_remove_group(struct mali_dlbu_core *dlbu, struct mali_group *group) +{ + struct mali_pp_core *pp_core; + u32 bcast_id; + + MALI_DEBUG_ASSERT_POINTER( dlbu ); + MALI_DEBUG_ASSERT_POINTER( group ); + + pp_core = mali_group_get_pp_core(group); + bcast_id = mali_pp_core_get_bcast_id(pp_core); + + dlbu->pp_cores_mask &= ~bcast_id; + MALI_DEBUG_PRINT(3, ("Mali DLBU: Removing core[%d] New mask= 0x%02x\n", bcast_id, dlbu->pp_cores_mask)); +} + +/* Configure the DLBU for \a job. This needs to be done before the job is started on the groups in the DLBU. */ +void mali_dlbu_config_job(struct mali_dlbu_core *dlbu, struct mali_pp_job *job) +{ + u32 *registers; + MALI_DEBUG_ASSERT(job); + registers = mali_pp_job_get_dlbu_registers(job); + MALI_DEBUG_PRINT(4, ("Mali DLBU: Starting job\n")); + + /* Writing 4 registers: + * DLBU registers except the first two (written once at DLBU initialisation / reset) and the PP_ENABLE_MASK register */ + mali_hw_core_register_write_array_relaxed(&dlbu->hw_core, MALI_DLBU_REGISTER_TLLIST_VBASEADDR, registers, 4); + +} diff --git a/drivers/gpu/arm/mali/common/mali_dlbu.h b/drivers/gpu/arm/mali/common/mali_dlbu.h new file mode 100644 index 00000000000000..8fbfc35baf03f2 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_dlbu.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_DLBU_H__ +#define __MALI_DLBU_H__ + +#define MALI_DLBU_VIRT_ADDR 0xFFF00000 /* master tile virtual address fixed at this value and mapped into every session */ + +#include "mali_osk.h" + +struct mali_pp_job; +struct mali_group; + +extern u32 mali_dlbu_phys_addr; + +struct mali_dlbu_core; + +_mali_osk_errcode_t mali_dlbu_initialize(void); +void mali_dlbu_terminate(void); + +struct mali_dlbu_core *mali_dlbu_create(const _mali_osk_resource_t * resource); +void mali_dlbu_delete(struct mali_dlbu_core *dlbu); + +_mali_osk_errcode_t mali_dlbu_reset(struct mali_dlbu_core *dlbu); + +void mali_dlbu_add_group(struct mali_dlbu_core *dlbu, struct mali_group *group); +void mali_dlbu_remove_group(struct mali_dlbu_core *dlbu, struct mali_group *group); + +/** @brief Called to update HW after DLBU state changed + * + * This function must be called after \a mali_dlbu_add_group or \a + * mali_dlbu_remove_group to write the updated mask to hardware, unless the + * same is accomplished by calling \a mali_dlbu_reset. + */ +void mali_dlbu_update_mask(struct mali_dlbu_core *dlbu); + +void mali_dlbu_config_job(struct mali_dlbu_core *dlbu, struct mali_pp_job *job); + +#endif /* __MALI_DLBU_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_dma.c b/drivers/gpu/arm/mali/common/mali_dma.c new file mode 100644 index 00000000000000..a5296264d00427 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_dma.c @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_hw_core.h" +#include "mali_dma.h" + +/** + * Size of the Mali-450 DMA unit registers in bytes. + */ +#define MALI450_DMA_REG_SIZE 0x08 + +/** + * Value that appears in MEMSIZE if an error occurs when reading the command list. + */ +#define MALI450_DMA_BUS_ERR_VAL 0xffffffff + +/** + * Mali DMA registers + * Used in the register read/write routines. + * See the hardware documentation for more information about each register. + */ +typedef enum mali_dma_register { + + MALI450_DMA_REG_SOURCE_ADDRESS = 0x0000, + MALI450_DMA_REG_SOURCE_SIZE = 0x0004, +} mali_dma_register; + +struct mali_dma_core { + struct mali_hw_core hw_core; /**< Common for all HW cores */ + _mali_osk_spinlock_t *lock; /**< Lock protecting access to DMA core */ + mali_dma_pool pool; /**< Memory pool for command buffers */ +}; + +static struct mali_dma_core *mali_global_dma_core = NULL; + +struct mali_dma_core *mali_dma_create(_mali_osk_resource_t *resource) +{ + struct mali_dma_core* dma; + _mali_osk_errcode_t err; + + MALI_DEBUG_ASSERT(NULL == mali_global_dma_core); + + dma = _mali_osk_malloc(sizeof(struct mali_dma_core)); + if (dma == NULL) goto alloc_failed; + + dma->lock = _mali_osk_spinlock_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_DMA_COMMAND); + if (NULL == dma->lock) goto lock_init_failed; + + dma->pool = mali_dma_pool_create(MALI_DMA_CMD_BUF_SIZE, 4, 0); + if (NULL == dma->pool) goto dma_pool_failed; + + err = mali_hw_core_create(&dma->hw_core, resource, MALI450_DMA_REG_SIZE); + if (_MALI_OSK_ERR_OK != err) goto hw_core_failed; + + mali_global_dma_core = dma; + MALI_DEBUG_PRINT(2, ("Mali DMA: Created Mali APB DMA unit\n")); + return dma; + + /* Error handling */ + +hw_core_failed: + mali_dma_pool_destroy(dma->pool); +dma_pool_failed: + _mali_osk_spinlock_term(dma->lock); +lock_init_failed: + _mali_osk_free(dma); +alloc_failed: + MALI_DEBUG_PRINT(2, ("Mali DMA: Failed to create APB DMA unit\n")); + return NULL; +} + +void mali_dma_delete(struct mali_dma_core *dma) +{ + MALI_DEBUG_ASSERT_POINTER(dma); + + MALI_DEBUG_PRINT(2, ("Mali DMA: Deleted Mali APB DMA unit\n")); + + mali_hw_core_delete(&dma->hw_core); + _mali_osk_spinlock_term(dma->lock); + mali_dma_pool_destroy(dma->pool); + _mali_osk_free(dma); +} + +static void mali_dma_bus_error(struct mali_dma_core *dma) +{ + u32 addr = mali_hw_core_register_read(&dma->hw_core, MALI450_DMA_REG_SOURCE_ADDRESS); + + MALI_PRINT_ERROR(("Mali DMA: Bus error when reading command list from 0x%lx\n", addr)); + + /* Clear the bus error */ + mali_hw_core_register_write(&dma->hw_core, MALI450_DMA_REG_SOURCE_SIZE, 0); +} + +static mali_bool mali_dma_is_busy(struct mali_dma_core *dma) +{ + u32 val; + mali_bool dma_busy_flag = MALI_FALSE; + + MALI_DEBUG_ASSERT_POINTER(dma); + + val = mali_hw_core_register_read(&dma->hw_core, MALI450_DMA_REG_SOURCE_SIZE); + + if (MALI450_DMA_BUS_ERR_VAL == val) { + /* Bus error reading command list */ + mali_dma_bus_error(dma); + return MALI_FALSE; + } + if (val > 0) { + dma_busy_flag = MALI_TRUE; + } + + return dma_busy_flag; +} + +static void mali_dma_start_transfer(struct mali_dma_core* dma, mali_dma_cmd_buf *buf) +{ + u32 memsize = buf->size * 4; + u32 addr = buf->phys_addr; + + MALI_DEBUG_ASSERT_POINTER(dma); + MALI_DEBUG_ASSERT(memsize < (1 << 16)); + MALI_DEBUG_ASSERT(0 == (memsize & 0x3)); /* 4 byte aligned */ + + MALI_DEBUG_ASSERT(!mali_dma_is_busy(dma)); + + /* Writes the physical source memory address of chunk containing command headers and data */ + mali_hw_core_register_write(&dma->hw_core, MALI450_DMA_REG_SOURCE_ADDRESS, addr); + + /* Writes the length of transfer */ + mali_hw_core_register_write(&dma->hw_core, MALI450_DMA_REG_SOURCE_SIZE, memsize); +} + +_mali_osk_errcode_t mali_dma_get_cmd_buf(mali_dma_cmd_buf *buf) +{ + MALI_DEBUG_ASSERT_POINTER(buf); + + buf->virt_addr = (u32*)mali_dma_pool_alloc(mali_global_dma_core->pool, &buf->phys_addr); + if (NULL == buf->virt_addr) { + return _MALI_OSK_ERR_NOMEM; + } + + /* size contains the number of words in the buffer and is incremented + * as commands are added to the buffer. */ + buf->size = 0; + + return _MALI_OSK_ERR_OK; +} + +void mali_dma_put_cmd_buf(mali_dma_cmd_buf *buf) +{ + MALI_DEBUG_ASSERT_POINTER(buf); + + if (NULL == buf->virt_addr) return; + + mali_dma_pool_free(mali_global_dma_core->pool, buf->virt_addr, buf->phys_addr); + + buf->virt_addr = NULL; +} + +_mali_osk_errcode_t mali_dma_start(struct mali_dma_core* dma, mali_dma_cmd_buf *buf) +{ + _mali_osk_errcode_t err = _MALI_OSK_ERR_OK; + + _mali_osk_spinlock_lock(dma->lock); + + if (mali_dma_is_busy(dma)) { + err = _MALI_OSK_ERR_BUSY; + goto out; + } + + mali_dma_start_transfer(dma, buf); + +out: + _mali_osk_spinlock_unlock(dma->lock); + return err; +} + +void mali_dma_debug(struct mali_dma_core *dma) +{ + MALI_DEBUG_ASSERT_POINTER(dma); + MALI_DEBUG_PRINT(1, ("DMA unit registers:\n\t%08x, %08x\n", + mali_hw_core_register_read(&dma->hw_core, MALI450_DMA_REG_SOURCE_ADDRESS), + mali_hw_core_register_read(&dma->hw_core, MALI450_DMA_REG_SOURCE_SIZE) + )); + +} + +struct mali_dma_core *mali_dma_get_global_dma_core(void) +{ + /* Returns the global dma core object */ + return mali_global_dma_core; +} diff --git a/drivers/gpu/arm/mali/common/mali_dma.h b/drivers/gpu/arm/mali/common/mali_dma.h new file mode 100644 index 00000000000000..93b9ca006e9e68 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_dma.h @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_DMA_H__ +#define __MALI_DMA_H__ + +#include "mali_osk.h" +#include "mali_osk_mali.h" +#include "mali_hw_core.h" + +#define MALI_DMA_CMD_BUF_SIZE 1024 + +typedef struct mali_dma_cmd_buf { + u32 *virt_addr; /**< CPU address of command buffer */ + u32 phys_addr; /**< Physical address of command buffer */ + u32 size; /**< Number of prepared words in command buffer */ +} mali_dma_cmd_buf; + +/** @brief Create a new DMA unit + * + * This is called from entry point of the driver in order to create and + * intialize the DMA resource + * + * @param resource it will be a pointer to a DMA resource + * @return DMA object on success, NULL on failure + */ +struct mali_dma_core *mali_dma_create(_mali_osk_resource_t *resource); + +/** @brief Delete DMA unit + * + * This is called on entry point of driver if the driver initialization fails + * after initialization of the DMA unit. It is also called on the exit of the + * driver to delete the DMA resource + * + * @param dma Pointer to DMA unit object + */ +void mali_dma_delete(struct mali_dma_core *dma); + +/** @brief Retrieves the MALI DMA core object (if there is) + * + * @return The Mali DMA object otherwise NULL + */ +struct mali_dma_core *mali_dma_get_global_dma_core(void); + +/** + * @brief Run a command buffer on the DMA unit + * + * @param dma Pointer to the DMA unit to use + * @param buf Pointer to the command buffer to use + * @return _MALI_OSK_ERR_OK if the buffer was started successfully, + * _MALI_OSK_ERR_BUSY if the DMA unit is busy. + */ +_mali_osk_errcode_t mali_dma_start(struct mali_dma_core* dma, mali_dma_cmd_buf *buf); + +/** + * @brief Create a DMA command + * + * @param core Mali core + * @param reg offset to register of core + * @param n number of registers to write + */ +MALI_STATIC_INLINE u32 mali_dma_command_write(struct mali_hw_core *core, u32 reg, u32 n) +{ + u32 core_offset = core->phys_offset; + + MALI_DEBUG_ASSERT(reg < 0x2000); + MALI_DEBUG_ASSERT(n < 0x800); + MALI_DEBUG_ASSERT(core_offset < 0x30000); + MALI_DEBUG_ASSERT(0 == ((core_offset + reg) & ~0x7FFFF)); + + return (n << 20) | (core_offset + reg); +} + +/** + * @brief Add a array write to DMA command buffer + * + * @param buf DMA command buffer to fill in + * @param core Core to do DMA to + * @param reg Register on core to start writing to + * @param data Pointer to data to write + * @param count Number of 4 byte words to write + */ +MALI_STATIC_INLINE void mali_dma_write_array(mali_dma_cmd_buf *buf, struct mali_hw_core *core, + u32 reg, u32 *data, u32 count) +{ + MALI_DEBUG_ASSERT((buf->size + 1 + count ) < MALI_DMA_CMD_BUF_SIZE / 4); + + buf->virt_addr[buf->size++] = mali_dma_command_write(core, reg, count); + + _mali_osk_memcpy(buf->virt_addr + buf->size, data, count * sizeof(*buf->virt_addr)); + + buf->size += count; +} + +/** + * @brief Add a conditional array write to DMA command buffer + * + * @param buf DMA command buffer to fill in + * @param core Core to do DMA to + * @param reg Register on core to start writing to + * @param data Pointer to data to write + * @param count Number of 4 byte words to write + * @param ref Pointer to referance data that can be skipped if equal + */ +MALI_STATIC_INLINE void mali_dma_write_array_conditional(mali_dma_cmd_buf *buf, struct mali_hw_core *core, + u32 reg, u32 *data, u32 count, const u32 *ref) +{ + /* Do conditional array writes are not yet implemented, fallback to a + * normal array write. */ + mali_dma_write_array(buf, core, reg, data, count); +} + +/** + * @brief Add a conditional register write to the DMA command buffer + * + * If the data matches the reference the command will be skipped. + * + * @param buf DMA command buffer to fill in + * @param core Core to do DMA to + * @param reg Register on core to start writing to + * @param data Pointer to data to write + * @param ref Pointer to referance data that can be skipped if equal + */ +MALI_STATIC_INLINE void mali_dma_write_conditional(mali_dma_cmd_buf *buf, struct mali_hw_core *core, + u32 reg, u32 data, const u32 ref) +{ + /* Skip write if reference value is equal to data. */ + if (data == ref) return; + + buf->virt_addr[buf->size++] = mali_dma_command_write(core, reg, 1); + + buf->virt_addr[buf->size++] = data; + + MALI_DEBUG_ASSERT(buf->size < MALI_DMA_CMD_BUF_SIZE / 4); +} + +/** + * @brief Add a register write to the DMA command buffer + * + * @param buf DMA command buffer to fill in + * @param core Core to do DMA to + * @param reg Register on core to start writing to + * @param data Pointer to data to write + */ +MALI_STATIC_INLINE void mali_dma_write(mali_dma_cmd_buf *buf, struct mali_hw_core *core, + u32 reg, u32 data) +{ + buf->virt_addr[buf->size++] = mali_dma_command_write(core, reg, 1); + + buf->virt_addr[buf->size++] = data; + + MALI_DEBUG_ASSERT(buf->size < MALI_DMA_CMD_BUF_SIZE / 4); +} + +/** + * @brief Prepare DMA command buffer for use + * + * This function allocates the DMA buffer itself. + * + * @param buf The mali_dma_cmd_buf to prepare + * @return _MALI_OSK_ERR_OK if the \a buf is ready to use + */ +_mali_osk_errcode_t mali_dma_get_cmd_buf(mali_dma_cmd_buf *buf); + +/** + * @brief Check if a DMA command buffer is ready for use + * + * @param buf The mali_dma_cmd_buf to check + * @return MALI_TRUE if buffer is usable, MALI_FALSE otherwise + */ +MALI_STATIC_INLINE mali_bool mali_dma_cmd_buf_is_valid(mali_dma_cmd_buf *buf) +{ + return NULL != buf->virt_addr; +} + +/** + * @brief Return a DMA command buffer + * + * @param buf Pointer to DMA command buffer to return + */ +void mali_dma_put_cmd_buf(mali_dma_cmd_buf *buf); + +#endif /* __MALI_DMA_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_gp.c b/drivers/gpu/arm/mali/common/mali_gp.c new file mode 100644 index 00000000000000..a3982b85cdb944 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_gp.c @@ -0,0 +1,337 @@ +/* + * Copyright (C) 2011-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_gp.h" +#include "mali_hw_core.h" +#include "mali_group.h" +#include "mali_osk.h" +#include "regs/mali_gp_regs.h" +#include "mali_kernel_common.h" +#include "mali_kernel_core.h" +#if defined(CONFIG_MALI400_PROFILING) +#include "mali_osk_profiling.h" +#endif + +static struct mali_gp_core *mali_global_gp_core = NULL; + +/* Interrupt handlers */ +static void mali_gp_irq_probe_trigger(void *data); +static _mali_osk_errcode_t mali_gp_irq_probe_ack(void *data); + +struct mali_gp_core *mali_gp_create(const _mali_osk_resource_t * resource, struct mali_group *group) +{ + struct mali_gp_core* core = NULL; + + MALI_DEBUG_ASSERT(NULL == mali_global_gp_core); + MALI_DEBUG_PRINT(2, ("Mali GP: Creating Mali GP core: %s\n", resource->description)); + + core = _mali_osk_malloc(sizeof(struct mali_gp_core)); + if (NULL != core) { + if (_MALI_OSK_ERR_OK == mali_hw_core_create(&core->hw_core, resource, MALIGP2_REGISTER_ADDRESS_SPACE_SIZE)) { + _mali_osk_errcode_t ret; + + ret = mali_gp_reset(core); + + if (_MALI_OSK_ERR_OK == ret) { + ret = mali_group_add_gp_core(group, core); + if (_MALI_OSK_ERR_OK == ret) { + /* Setup IRQ handlers (which will do IRQ probing if needed) */ + core->irq = _mali_osk_irq_init(resource->irq, + mali_group_upper_half_gp, + group, + mali_gp_irq_probe_trigger, + mali_gp_irq_probe_ack, + core, + resource->description); + if (NULL != core->irq) { + MALI_DEBUG_PRINT(4, ("Mali GP: set global gp core from 0x%08X to 0x%08X\n", mali_global_gp_core, core)); + mali_global_gp_core = core; + + return core; + } else { + MALI_PRINT_ERROR(("Mali GP: Failed to setup interrupt handlers for GP core %s\n", core->hw_core.description)); + } + mali_group_remove_gp_core(group); + } else { + MALI_PRINT_ERROR(("Mali GP: Failed to add core %s to group\n", core->hw_core.description)); + } + } + mali_hw_core_delete(&core->hw_core); + } + + _mali_osk_free(core); + } else { + MALI_PRINT_ERROR(("Failed to allocate memory for GP core\n")); + } + + return NULL; +} + +void mali_gp_delete(struct mali_gp_core *core) +{ + MALI_DEBUG_ASSERT_POINTER(core); + + _mali_osk_irq_term(core->irq); + mali_hw_core_delete(&core->hw_core); + mali_global_gp_core = NULL; + _mali_osk_free(core); +} + +void mali_gp_stop_bus(struct mali_gp_core *core) +{ + MALI_DEBUG_ASSERT_POINTER(core); + + mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_CMD, MALIGP2_REG_VAL_CMD_STOP_BUS); +} + +_mali_osk_errcode_t mali_gp_stop_bus_wait(struct mali_gp_core *core) +{ + int i; + + MALI_DEBUG_ASSERT_POINTER(core); + + /* Send the stop bus command. */ + mali_gp_stop_bus(core); + + /* Wait for bus to be stopped */ + for (i = 0; i < MALI_REG_POLL_COUNT_FAST; i++) { + if (mali_hw_core_register_read(&core->hw_core, MALIGP2_REG_ADDR_MGMT_STATUS) & MALIGP2_REG_VAL_STATUS_BUS_STOPPED) { + break; + } + } + + if (MALI_REG_POLL_COUNT_FAST == i) { + MALI_PRINT_ERROR(("Mali GP: Failed to stop bus on %s\n", core->hw_core.description)); + return _MALI_OSK_ERR_FAULT; + } + return _MALI_OSK_ERR_OK; +} + +void mali_gp_hard_reset(struct mali_gp_core *core) +{ + const u32 reset_wait_target_register = MALIGP2_REG_ADDR_MGMT_WRITE_BOUND_LOW; + const u32 reset_invalid_value = 0xC0FFE000; + const u32 reset_check_value = 0xC01A0000; + const u32 reset_default_value = 0; + int i; + + MALI_DEBUG_ASSERT_POINTER(core); + MALI_DEBUG_PRINT(4, ("Mali GP: Hard reset of core %s\n", core->hw_core.description)); + + mali_hw_core_register_write(&core->hw_core, reset_wait_target_register, reset_invalid_value); + + mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_CMD, MALIGP2_REG_VAL_CMD_RESET); + + for (i = 0; i < MALI_REG_POLL_COUNT_FAST; i++) { + mali_hw_core_register_write(&core->hw_core, reset_wait_target_register, reset_check_value); + if (reset_check_value == mali_hw_core_register_read(&core->hw_core, reset_wait_target_register)) { + break; + } + } + + if (MALI_REG_POLL_COUNT_FAST == i) { + MALI_PRINT_ERROR(("Mali GP: The hard reset loop didn't work, unable to recover\n")); + } + + mali_hw_core_register_write(&core->hw_core, reset_wait_target_register, reset_default_value); /* set it back to the default */ + /* Re-enable interrupts */ + mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_CLEAR, MALIGP2_REG_VAL_IRQ_MASK_ALL); + mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_MASK, MALIGP2_REG_VAL_IRQ_MASK_USED); + +} + +void mali_gp_reset_async(struct mali_gp_core *core) +{ + MALI_DEBUG_ASSERT_POINTER(core); + + MALI_DEBUG_PRINT(4, ("Mali GP: Reset of core %s\n", core->hw_core.description)); + + mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_MASK, 0); /* disable the IRQs */ + mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_CLEAR, MALI400GP_REG_VAL_IRQ_RESET_COMPLETED); + mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_CMD, MALI400GP_REG_VAL_CMD_SOFT_RESET); + +} + +_mali_osk_errcode_t mali_gp_reset_wait(struct mali_gp_core *core) +{ + int i; + u32 rawstat = 0; + + MALI_DEBUG_ASSERT_POINTER(core); + + for (i = 0; i < MALI_REG_POLL_COUNT_FAST; i++) { + rawstat = mali_hw_core_register_read(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_RAWSTAT); + if (rawstat & MALI400GP_REG_VAL_IRQ_RESET_COMPLETED) { + break; + } + } + + if (i == MALI_REG_POLL_COUNT_FAST) { + MALI_PRINT_ERROR(("Mali GP: Failed to reset core %s, rawstat: 0x%08x\n", + core->hw_core.description, rawstat)); + return _MALI_OSK_ERR_FAULT; + } + + /* Re-enable interrupts */ + mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_CLEAR, MALIGP2_REG_VAL_IRQ_MASK_ALL); + mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_MASK, MALIGP2_REG_VAL_IRQ_MASK_USED); + + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t mali_gp_reset(struct mali_gp_core *core) +{ + mali_gp_reset_async(core); + return mali_gp_reset_wait(core); +} + +void mali_gp_job_start(struct mali_gp_core *core, struct mali_gp_job *job) +{ + u32 startcmd = 0; + u32 *frame_registers = mali_gp_job_get_frame_registers(job); + u32 counter_src0 = mali_gp_job_get_perf_counter_src0(job); + u32 counter_src1 = mali_gp_job_get_perf_counter_src1(job); + + MALI_DEBUG_ASSERT_POINTER(core); + + if (mali_gp_job_has_vs_job(job)) { + startcmd |= (u32) MALIGP2_REG_VAL_CMD_START_VS; + } + + if (mali_gp_job_has_plbu_job(job)) { + startcmd |= (u32) MALIGP2_REG_VAL_CMD_START_PLBU; + } + + MALI_DEBUG_ASSERT(0 != startcmd); + + mali_hw_core_register_write_array_relaxed(&core->hw_core, MALIGP2_REG_ADDR_MGMT_VSCL_START_ADDR, frame_registers, MALIGP2_NUM_REGS_FRAME); + + if (MALI_HW_CORE_NO_COUNTER != counter_src0) { + mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_SRC, counter_src0); + mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_ENABLE, MALIGP2_REG_VAL_PERF_CNT_ENABLE); + } + if (MALI_HW_CORE_NO_COUNTER != counter_src1) { + mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_SRC, counter_src1); + mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_ENABLE, MALIGP2_REG_VAL_PERF_CNT_ENABLE); + } + + MALI_DEBUG_PRINT(3, ("Mali GP: Starting job (0x%08x) on core %s with command 0x%08X\n", job, core->hw_core.description, startcmd)); + + mali_hw_core_register_write_relaxed(&core->hw_core, MALIGP2_REG_ADDR_MGMT_CMD, MALIGP2_REG_VAL_CMD_UPDATE_PLBU_ALLOC); + + /* Barrier to make sure the previous register write is finished */ + _mali_osk_write_mem_barrier(); + + /* This is the command that starts the core. */ + mali_hw_core_register_write_relaxed(&core->hw_core, MALIGP2_REG_ADDR_MGMT_CMD, startcmd); + + /* Barrier to make sure the previous register write is finished */ + _mali_osk_write_mem_barrier(); +} + +void mali_gp_resume_with_new_heap(struct mali_gp_core *core, u32 start_addr, u32 end_addr) +{ + u32 irq_readout; + + MALI_DEBUG_ASSERT_POINTER(core); + + irq_readout = mali_hw_core_register_read(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_RAWSTAT); + + if (irq_readout & MALIGP2_REG_VAL_IRQ_PLBU_OUT_OF_MEM) { + mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_CLEAR, (MALIGP2_REG_VAL_IRQ_PLBU_OUT_OF_MEM | MALIGP2_REG_VAL_IRQ_HANG)); + mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_MASK, MALIGP2_REG_VAL_IRQ_MASK_USED); /* re-enable interrupts */ + mali_hw_core_register_write_relaxed(&core->hw_core, MALIGP2_REG_ADDR_MGMT_PLBU_ALLOC_START_ADDR, start_addr); + mali_hw_core_register_write_relaxed(&core->hw_core, MALIGP2_REG_ADDR_MGMT_PLBU_ALLOC_END_ADDR, end_addr); + + MALI_DEBUG_PRINT(3, ("Mali GP: Resuming job\n")); + + mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_CMD, MALIGP2_REG_VAL_CMD_UPDATE_PLBU_ALLOC); + _mali_osk_write_mem_barrier(); + } + /* + * else: core has been reset between PLBU_OUT_OF_MEM interrupt and this new heap response. + * A timeout or a page fault on Mali-200 PP core can cause this behaviour. + */ +} + +u32 mali_gp_core_get_version(struct mali_gp_core *core) +{ + MALI_DEBUG_ASSERT_POINTER(core); + return mali_hw_core_register_read(&core->hw_core, MALIGP2_REG_ADDR_MGMT_VERSION); +} + +struct mali_gp_core *mali_gp_get_global_gp_core(void) +{ + return mali_global_gp_core; +} + +/* ------------- interrupt handling below ------------------ */ +static void mali_gp_irq_probe_trigger(void *data) +{ + struct mali_gp_core *core = (struct mali_gp_core *)data; + + mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_MASK, MALIGP2_REG_VAL_IRQ_MASK_USED); + mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_RAWSTAT, MALIGP2_REG_VAL_CMD_FORCE_HANG); + _mali_osk_mem_barrier(); +} + +static _mali_osk_errcode_t mali_gp_irq_probe_ack(void *data) +{ + struct mali_gp_core *core = (struct mali_gp_core *)data; + u32 irq_readout; + + irq_readout = mali_hw_core_register_read(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_STAT); + if (MALIGP2_REG_VAL_IRQ_FORCE_HANG & irq_readout) { + mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_CLEAR, MALIGP2_REG_VAL_IRQ_FORCE_HANG); + _mali_osk_mem_barrier(); + return _MALI_OSK_ERR_OK; + } + + return _MALI_OSK_ERR_FAULT; +} + +/* ------ local helper functions below --------- */ +#if MALI_STATE_TRACKING +u32 mali_gp_dump_state(struct mali_gp_core *core, char *buf, u32 size) +{ + int n = 0; + + n += _mali_osk_snprintf(buf + n, size - n, "\tGP: %s\n", core->hw_core.description); + + return n; +} +#endif + +void mali_gp_update_performance_counters(struct mali_gp_core *core, struct mali_gp_job *job, mali_bool suspend) +{ + u32 val0 = 0; + u32 val1 = 0; + u32 counter_src0 = mali_gp_job_get_perf_counter_src0(job); + u32 counter_src1 = mali_gp_job_get_perf_counter_src1(job); + + if (MALI_HW_CORE_NO_COUNTER != counter_src0) { + val0 = mali_hw_core_register_read(&core->hw_core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_VALUE); + mali_gp_job_set_perf_counter_value0(job, val0); + +#if defined(CONFIG_MALI400_PROFILING) + _mali_osk_profiling_report_hw_counter(COUNTER_VP_0_C0, val0); +#endif + + } + + if (MALI_HW_CORE_NO_COUNTER != counter_src1) { + val1 = mali_hw_core_register_read(&core->hw_core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_VALUE); + mali_gp_job_set_perf_counter_value1(job, val1); + +#if defined(CONFIG_MALI400_PROFILING) + _mali_osk_profiling_report_hw_counter(COUNTER_VP_0_C1, val1); +#endif + } +} diff --git a/drivers/gpu/arm/mali/common/mali_gp.h b/drivers/gpu/arm/mali/common/mali_gp.h new file mode 100644 index 00000000000000..5450c568ffc2ec --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_gp.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2011-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_GP_H__ +#define __MALI_GP_H__ + +#include "mali_osk.h" +#include "mali_gp_job.h" +#include "mali_hw_core.h" +#include "regs/mali_gp_regs.h" + +struct mali_group; + +/** + * Definition of the GP core struct + * Used to track a GP core in the system. + */ +struct mali_gp_core { + struct mali_hw_core hw_core; /**< Common for all HW cores */ + _mali_osk_irq_t *irq; /**< IRQ handler */ +}; + +_mali_osk_errcode_t mali_gp_initialize(void); +void mali_gp_terminate(void); + +struct mali_gp_core *mali_gp_create(const _mali_osk_resource_t * resource, struct mali_group *group); +void mali_gp_delete(struct mali_gp_core *core); + +void mali_gp_stop_bus(struct mali_gp_core *core); +_mali_osk_errcode_t mali_gp_stop_bus_wait(struct mali_gp_core *core); +void mali_gp_reset_async(struct mali_gp_core *core); +_mali_osk_errcode_t mali_gp_reset_wait(struct mali_gp_core *core); +void mali_gp_hard_reset(struct mali_gp_core *core); +_mali_osk_errcode_t mali_gp_reset(struct mali_gp_core *core); + +void mali_gp_job_start(struct mali_gp_core *core, struct mali_gp_job *job); +void mali_gp_resume_with_new_heap(struct mali_gp_core *core, u32 start_addr, u32 end_addr); + +u32 mali_gp_core_get_version(struct mali_gp_core *core); + +struct mali_gp_core *mali_gp_get_global_gp_core(void); + +u32 mali_gp_dump_state(struct mali_gp_core *core, char *buf, u32 size); + +void mali_gp_update_performance_counters(struct mali_gp_core *core, struct mali_gp_job *job, mali_bool suspend); + +/*** Accessor functions ***/ +MALI_STATIC_INLINE const char *mali_gp_get_hw_core_desc(struct mali_gp_core *core) +{ + return core->hw_core.description; +} + +/*** Register reading/writing functions ***/ +MALI_STATIC_INLINE u32 mali_gp_get_int_stat(struct mali_gp_core *core) +{ + return mali_hw_core_register_read(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_STAT); +} + +MALI_STATIC_INLINE void mali_gp_mask_all_interrupts(struct mali_gp_core *core) +{ + mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_MASK, MALIGP2_REG_VAL_IRQ_MASK_NONE); +} + +MALI_STATIC_INLINE u32 mali_gp_read_rawstat(struct mali_gp_core *core) +{ + return mali_hw_core_register_read(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_RAWSTAT) & MALIGP2_REG_VAL_IRQ_MASK_USED; +} + +MALI_STATIC_INLINE u32 mali_gp_read_core_status(struct mali_gp_core *core) +{ + return mali_hw_core_register_read(&core->hw_core, MALIGP2_REG_ADDR_MGMT_STATUS); +} + +MALI_STATIC_INLINE void mali_gp_enable_interrupts(struct mali_gp_core *core, u32 irq_exceptions) +{ + /* Enable all interrupts, except those specified in irq_exceptions */ + mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_MASK, + MALIGP2_REG_VAL_IRQ_MASK_USED & ~irq_exceptions); +} + +MALI_STATIC_INLINE u32 mali_gp_read_plbu_alloc_start_addr(struct mali_gp_core *core) +{ + return mali_hw_core_register_read(&core->hw_core, MALIGP2_REG_ADDR_MGMT_PLBU_ALLOC_START_ADDR); +} + +#endif /* __MALI_GP_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_gp_job.c b/drivers/gpu/arm/mali/common/mali_gp_job.c new file mode 100644 index 00000000000000..a966f57cae5adf --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_gp_job.c @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2011-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_gp_job.h" +#include "mali_osk.h" +#include "mali_osk_list.h" +#include "mali_uk_types.h" + +static u32 gp_counter_src0 = MALI_HW_CORE_NO_COUNTER; /**< Performance counter 0, MALI_HW_CORE_NO_COUNTER for disabled */ +static u32 gp_counter_src1 = MALI_HW_CORE_NO_COUNTER; /**< Performance counter 1, MALI_HW_CORE_NO_COUNTER for disabled */ + +struct mali_gp_job *mali_gp_job_create(struct mali_session_data *session, _mali_uk_gp_start_job_s *uargs, u32 id, struct mali_timeline_tracker *pp_tracker) +{ + struct mali_gp_job *job; + u32 perf_counter_flag; + + job = _mali_osk_malloc(sizeof(struct mali_gp_job)); + if (NULL != job) { + job->finished_notification = _mali_osk_notification_create(_MALI_NOTIFICATION_GP_FINISHED, sizeof(_mali_uk_gp_job_finished_s)); + if (NULL == job->finished_notification) { + _mali_osk_free(job); + return NULL; + } + + job->oom_notification = _mali_osk_notification_create(_MALI_NOTIFICATION_GP_STALLED, sizeof(_mali_uk_gp_job_suspended_s)); + if (NULL == job->oom_notification) { + _mali_osk_notification_delete(job->finished_notification); + _mali_osk_free(job); + return NULL; + } + + if (0 != _mali_osk_copy_from_user(&job->uargs, uargs, sizeof(_mali_uk_gp_start_job_s))) { + _mali_osk_notification_delete(job->finished_notification); + _mali_osk_notification_delete(job->oom_notification); + _mali_osk_free(job); + return NULL; + } + + perf_counter_flag = mali_gp_job_get_perf_counter_flag(job); + + /* case when no counters came from user space + * so pass the debugfs / DS-5 provided global ones to the job object */ + if (!((perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_SRC0_ENABLE) || + (perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_SRC1_ENABLE))) { + mali_gp_job_set_perf_counter_src0(job, mali_gp_job_get_gp_counter_src0()); + mali_gp_job_set_perf_counter_src1(job, mali_gp_job_get_gp_counter_src1()); + } + + _mali_osk_list_init(&job->list); + job->session = session; + job->id = id; + job->heap_current_addr = job->uargs.frame_registers[4]; + job->perf_counter_value0 = 0; + job->perf_counter_value1 = 0; + job->pid = _mali_osk_get_pid(); + job->tid = _mali_osk_get_tid(); + + job->pp_tracker = pp_tracker; + if (NULL != job->pp_tracker) { + /* Take a reference on PP job's tracker that will be released when the GP + job is done. */ + mali_timeline_system_tracker_get(session->timeline_system, pp_tracker); + } + + mali_timeline_tracker_init(&job->tracker, MALI_TIMELINE_TRACKER_GP, NULL, job); + mali_timeline_fence_copy_uk_fence(&(job->tracker.fence), &(job->uargs.fence)); + + return job; + } + + return NULL; +} + +void mali_gp_job_delete(struct mali_gp_job *job) +{ + MALI_DEBUG_ASSERT_POINTER(job); + MALI_DEBUG_ASSERT(NULL == job->pp_tracker); + + /* de-allocate the pre-allocated oom notifications */ + if (NULL != job->oom_notification) { + _mali_osk_notification_delete(job->oom_notification); + job->oom_notification = NULL; + } + if (NULL != job->finished_notification) { + _mali_osk_notification_delete(job->finished_notification); + job->finished_notification = NULL; + } + + _mali_osk_free(job); +} + +u32 mali_gp_job_get_gp_counter_src0(void) +{ + return gp_counter_src0; +} + +void mali_gp_job_set_gp_counter_src0(u32 counter) +{ + gp_counter_src0 = counter; +} + +u32 mali_gp_job_get_gp_counter_src1(void) +{ + return gp_counter_src1; +} + +void mali_gp_job_set_gp_counter_src1(u32 counter) +{ + gp_counter_src1 = counter; +} + +mali_scheduler_mask mali_gp_job_signal_pp_tracker(struct mali_gp_job *job, mali_bool success) +{ + mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY; + + MALI_DEBUG_ASSERT_POINTER(job); + + if (NULL != job->pp_tracker) { + schedule_mask |= mali_timeline_system_tracker_put(job->session->timeline_system, job->pp_tracker, MALI_FALSE == success); + job->pp_tracker = NULL; + } + + return schedule_mask; +} diff --git a/drivers/gpu/arm/mali/common/mali_gp_job.h b/drivers/gpu/arm/mali/common/mali_gp_job.h new file mode 100644 index 00000000000000..1813c9d8e0818e --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_gp_job.h @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2011-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_GP_JOB_H__ +#define __MALI_GP_JOB_H__ + +#include "mali_osk.h" +#include "mali_osk_list.h" +#include "mali_uk_types.h" +#include "mali_session.h" +#include "mali_timeline.h" +#include "mali_scheduler_types.h" + +/** + * The structure represents a GP job, including all sub-jobs + * (This struct unfortunately needs to be public because of how the _mali_osk_list_* + * mechanism works) + */ +struct mali_gp_job { + _mali_osk_list_t list; /**< Used to link jobs together in the scheduler queue */ + struct mali_session_data *session; /**< Session which submitted this job */ + _mali_uk_gp_start_job_s uargs; /**< Arguments from user space */ + u32 id; /**< Identifier for this job in kernel space (sequential numbering) */ + u32 cache_order; /**< Cache order used for L2 cache flushing (sequential numbering) */ + u32 heap_current_addr; /**< Holds the current HEAP address when the job has completed */ + u32 perf_counter_value0; /**< Value of performance counter 0 (to be returned to user space) */ + u32 perf_counter_value1; /**< Value of performance counter 1 (to be returned to user space) */ + u32 pid; /**< Process ID of submitting process */ + u32 tid; /**< Thread ID of submitting thread */ + _mali_osk_notification_t *finished_notification; /**< Notification sent back to userspace on job complete */ + _mali_osk_notification_t *oom_notification; /**< Notification sent back to userspace on OOM */ + struct mali_timeline_tracker tracker; /**< Timeline tracker for this job */ + struct mali_timeline_tracker *pp_tracker; /**< Pointer to Timeline tracker for PP job that depends on this job. */ +}; + +struct mali_gp_job *mali_gp_job_create(struct mali_session_data *session, _mali_uk_gp_start_job_s *uargs, u32 id, struct mali_timeline_tracker *pp_tracker); +void mali_gp_job_delete(struct mali_gp_job *job); + +u32 mali_gp_job_get_gp_counter_src0(void); +void mali_gp_job_set_gp_counter_src0(u32 counter); +u32 mali_gp_job_get_gp_counter_src1(void); +void mali_gp_job_set_gp_counter_src1(u32 counter); + +MALI_STATIC_INLINE u32 mali_gp_job_get_id(struct mali_gp_job *job) +{ + return (NULL == job) ? 0 : job->id; +} + +MALI_STATIC_INLINE u32 mali_gp_job_get_cache_order(struct mali_gp_job *job) +{ + return (NULL == job) ? 0 : job->cache_order; +} + +MALI_STATIC_INLINE u32 mali_gp_job_get_user_id(struct mali_gp_job *job) +{ + return job->uargs.user_job_ptr; +} + +MALI_STATIC_INLINE u32 mali_gp_job_get_frame_builder_id(struct mali_gp_job *job) +{ + return job->uargs.frame_builder_id; +} + +MALI_STATIC_INLINE u32 mali_gp_job_get_flush_id(struct mali_gp_job *job) +{ + return job->uargs.flush_id; +} + +MALI_STATIC_INLINE u32 mali_gp_job_get_pid(struct mali_gp_job *job) +{ + return job->pid; +} + +MALI_STATIC_INLINE u32 mali_gp_job_get_tid(struct mali_gp_job *job) +{ + return job->tid; +} + +MALI_STATIC_INLINE u32* mali_gp_job_get_frame_registers(struct mali_gp_job *job) +{ + return job->uargs.frame_registers; +} + +MALI_STATIC_INLINE struct mali_session_data *mali_gp_job_get_session(struct mali_gp_job *job) +{ + return job->session; +} + +MALI_STATIC_INLINE mali_bool mali_gp_job_has_vs_job(struct mali_gp_job *job) +{ + return (job->uargs.frame_registers[0] != job->uargs.frame_registers[1]) ? MALI_TRUE : MALI_FALSE; +} + +MALI_STATIC_INLINE mali_bool mali_gp_job_has_plbu_job(struct mali_gp_job *job) +{ + return (job->uargs.frame_registers[2] != job->uargs.frame_registers[3]) ? MALI_TRUE : MALI_FALSE; +} + +MALI_STATIC_INLINE u32 mali_gp_job_get_current_heap_addr(struct mali_gp_job *job) +{ + return job->heap_current_addr; +} + +MALI_STATIC_INLINE void mali_gp_job_set_current_heap_addr(struct mali_gp_job *job, u32 heap_addr) +{ + job->heap_current_addr = heap_addr; +} + +MALI_STATIC_INLINE u32 mali_gp_job_get_perf_counter_flag(struct mali_gp_job *job) +{ + return job->uargs.perf_counter_flag; +} + +MALI_STATIC_INLINE u32 mali_gp_job_get_perf_counter_src0(struct mali_gp_job *job) +{ + return job->uargs.perf_counter_src0; +} + +MALI_STATIC_INLINE u32 mali_gp_job_get_perf_counter_src1(struct mali_gp_job *job) +{ + return job->uargs.perf_counter_src1; +} + +MALI_STATIC_INLINE u32 mali_gp_job_get_perf_counter_value0(struct mali_gp_job *job) +{ + return job->perf_counter_value0; +} + +MALI_STATIC_INLINE u32 mali_gp_job_get_perf_counter_value1(struct mali_gp_job *job) +{ + return job->perf_counter_value1; +} + +MALI_STATIC_INLINE void mali_gp_job_set_perf_counter_src0(struct mali_gp_job *job, u32 src) +{ + job->uargs.perf_counter_src0 = src; +} + +MALI_STATIC_INLINE void mali_gp_job_set_perf_counter_src1(struct mali_gp_job *job, u32 src) +{ + job->uargs.perf_counter_src1 = src; +} + +MALI_STATIC_INLINE void mali_gp_job_set_perf_counter_value0(struct mali_gp_job *job, u32 value) +{ + job->perf_counter_value0 = value; +} + +MALI_STATIC_INLINE void mali_gp_job_set_perf_counter_value1(struct mali_gp_job *job, u32 value) +{ + job->perf_counter_value1 = value; +} + +/** + * Returns MALI_TRUE if first job is after the second job, ordered by job ID. + * + * @param first First job. + * @param second Second job. + * @return MALI_TRUE if first job should be ordered after the second job, MALI_FALSE if not. + */ +MALI_STATIC_INLINE mali_bool mali_gp_job_is_after(struct mali_gp_job *first, struct mali_gp_job *second) +{ + /* A span is used to handle job ID wrapping. */ + return (mali_gp_job_get_id(first) - mali_gp_job_get_id(second)) < MALI_SCHEDULER_JOB_ID_SPAN; +} + +/** + * Release reference on tracker for PP job that depends on this GP job. + * + * @note If GP job has a reference on tracker, this function MUST be called before the GP job is + * deleted. + * + * @param job GP job that is done. + * @param success MALI_TRUE if job completed successfully, MALI_FALSE if not. + * @return A scheduling bitmask indicating whether scheduling needs to be done. + */ +mali_scheduler_mask mali_gp_job_signal_pp_tracker(struct mali_gp_job *job, mali_bool success); + +#endif /* __MALI_GP_JOB_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_gp_scheduler.c b/drivers/gpu/arm/mali/common/mali_gp_scheduler.c new file mode 100644 index 00000000000000..36a29e31f4c114 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_gp_scheduler.c @@ -0,0 +1,701 @@ +/* + * Copyright (C) 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_gp_scheduler.h" +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_osk_list.h" +#include "mali_scheduler.h" +#include "mali_gp.h" +#include "mali_gp_job.h" +#include "mali_group.h" +#include "mali_timeline.h" +#include "mali_osk_profiling.h" +#include "mali_kernel_utilization.h" +#if defined(CONFIG_GPU_TRACEPOINTS) && defined(CONFIG_TRACEPOINTS) +#include +#include +#endif + +enum mali_gp_slot_state { + MALI_GP_SLOT_STATE_IDLE, + MALI_GP_SLOT_STATE_WORKING, + MALI_GP_SLOT_STATE_DISABLED, +}; + +/* A render slot is an entity which jobs can be scheduled onto */ +struct mali_gp_slot { + struct mali_group *group; + /* + * We keep track of the state here as well as in the group object + * so we don't need to take the group lock so often (and also avoid clutter with the working lock) + */ + enum mali_gp_slot_state state; + u32 returned_cookie; +}; + +static u32 gp_version = 0; +static _MALI_OSK_LIST_HEAD_STATIC_INIT(job_queue); /* List of unscheduled jobs. */ +static _MALI_OSK_LIST_HEAD_STATIC_INIT(job_queue_high); /* List of unscheduled high priority jobs. */ +static struct mali_gp_slot slot; + +/* Variables to allow safe pausing of the scheduler */ +static _mali_osk_wait_queue_t *gp_scheduler_working_wait_queue = NULL; +static u32 pause_count = 0; + +static mali_bool mali_gp_scheduler_is_suspended(void *data); +static void mali_gp_scheduler_job_queued(void); +static void mali_gp_scheduler_job_completed(void); + +#if defined(MALI_UPPER_HALF_SCHEDULING) +static _mali_osk_spinlock_irq_t *gp_scheduler_lock = NULL; +#else +static _mali_osk_spinlock_t *gp_scheduler_lock = NULL; +#endif /* defined(MALI_UPPER_HALF_SCHEDULING) */ + +_mali_osk_errcode_t mali_gp_scheduler_initialize(void) +{ + u32 num_groups; + u32 i; + _mali_osk_errcode_t ret = _MALI_OSK_ERR_OK; + +#if defined(MALI_UPPER_HALF_SCHEDULING) + gp_scheduler_lock = _mali_osk_spinlock_irq_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_SCHEDULER); +#else + gp_scheduler_lock = _mali_osk_spinlock_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_SCHEDULER); +#endif /* defined(MALI_UPPER_HALF_SCHEDULING) */ + if (NULL == gp_scheduler_lock) { + ret = _MALI_OSK_ERR_NOMEM; + goto cleanup; + } + + gp_scheduler_working_wait_queue = _mali_osk_wait_queue_init(); + if (NULL == gp_scheduler_working_wait_queue) { + ret = _MALI_OSK_ERR_NOMEM; + goto cleanup; + } + + /* Find all the available GP cores */ + num_groups = mali_group_get_glob_num_groups(); + for (i = 0; i < num_groups; i++) { + struct mali_group *group = mali_group_get_glob_group(i); + MALI_DEBUG_ASSERT(NULL != group); + if (NULL != group) { + struct mali_gp_core *gp_core = mali_group_get_gp_core(group); + if (NULL != gp_core) { + if (0 == gp_version) { + /* Retrieve GP version */ + gp_version = mali_gp_core_get_version(gp_core); + } + slot.group = group; + slot.state = MALI_GP_SLOT_STATE_IDLE; + break; /* There is only one GP, no point in looking for more */ + } + } else { + ret = _MALI_OSK_ERR_ITEM_NOT_FOUND; + goto cleanup; + } + } + + return _MALI_OSK_ERR_OK; + +cleanup: + if (NULL != gp_scheduler_working_wait_queue) { + _mali_osk_wait_queue_term(gp_scheduler_working_wait_queue); + gp_scheduler_working_wait_queue = NULL; + } + + if (NULL != gp_scheduler_lock) { +#if defined(MALI_UPPER_HALF_SCHEDULING) + _mali_osk_spinlock_irq_term(gp_scheduler_lock); +#else + _mali_osk_spinlock_term(gp_scheduler_lock); +#endif /* defined(MALI_UPPER_HALF_SCHEDULING) */ + gp_scheduler_lock = NULL; + } + + return ret; +} + +void mali_gp_scheduler_terminate(void) +{ + MALI_DEBUG_ASSERT( MALI_GP_SLOT_STATE_IDLE == slot.state + || MALI_GP_SLOT_STATE_DISABLED == slot.state); + MALI_DEBUG_ASSERT_POINTER(slot.group); + mali_group_delete(slot.group); + + _mali_osk_wait_queue_term(gp_scheduler_working_wait_queue); + +#if defined(MALI_UPPER_HALF_SCHEDULING) + _mali_osk_spinlock_irq_term(gp_scheduler_lock); +#else + _mali_osk_spinlock_term(gp_scheduler_lock); +#endif /* defined(MALI_UPPER_HALF_SCHEDULING) */ +} + +MALI_STATIC_INLINE void mali_gp_scheduler_lock(void) +{ +#if defined(MALI_UPPER_HALF_SCHEDULING) + _mali_osk_spinlock_irq_lock(gp_scheduler_lock); +#else + _mali_osk_spinlock_lock(gp_scheduler_lock); +#endif /* defined(MALI_UPPER_HALF_SCHEDULING) */ + MALI_DEBUG_PRINT(5, ("Mali GP scheduler: GP scheduler lock taken\n")); +} + +MALI_STATIC_INLINE void mali_gp_scheduler_unlock(void) +{ + MALI_DEBUG_PRINT(5, ("Mali GP scheduler: Releasing GP scheduler lock\n")); +#if defined(MALI_UPPER_HALF_SCHEDULING) + _mali_osk_spinlock_irq_unlock(gp_scheduler_lock); +#else + _mali_osk_spinlock_unlock(gp_scheduler_lock); +#endif /* defined(MALI_UPPER_HALF_SCHEDULING) */ +} + +#if defined(DEBUG) +#define MALI_ASSERT_GP_SCHEDULER_LOCKED() MALI_DEBUG_ASSERT_LOCK_HELD(gp_scheduler_lock) +#else +#define MALI_ASSERT_GP_SCHEDULER_LOCKED() do {} while (0) +#endif /* defined(DEBUG) */ + +/* Group and scheduler must be locked when entering this function. Both will be unlocked before + * exiting. */ +static void mali_gp_scheduler_schedule_internal_and_unlock(void) +{ + struct mali_gp_job *job = NULL; + + MALI_DEBUG_ASSERT_LOCK_HELD(slot.group->lock); + MALI_DEBUG_ASSERT_LOCK_HELD(gp_scheduler_lock); + + if (0 < pause_count || MALI_GP_SLOT_STATE_IDLE != slot.state || + (_mali_osk_list_empty(&job_queue) && _mali_osk_list_empty(&job_queue_high))) { + mali_gp_scheduler_unlock(); + mali_group_unlock(slot.group); + MALI_DEBUG_PRINT(4, ("Mali GP scheduler: Nothing to schedule (paused=%u, idle slots=%u)\n", + pause_count, MALI_GP_SLOT_STATE_IDLE == slot.state ? 1 : 0)); +#if defined(CONFIG_GPU_TRACEPOINTS) && defined(CONFIG_TRACEPOINTS) + trace_gpu_sched_switch(mali_gp_get_hw_core_desc(group->gp_core), sched_clock(), 0, 0, 0); +#endif + return; /* Nothing to do, so early out */ + } + + /* Get next job in queue */ + if (!_mali_osk_list_empty(&job_queue_high)) { + job = _MALI_OSK_LIST_ENTRY(job_queue_high.next, struct mali_gp_job, list); + } else { + MALI_DEBUG_ASSERT(!_mali_osk_list_empty(&job_queue)); + job = _MALI_OSK_LIST_ENTRY(job_queue.next, struct mali_gp_job, list); + } + + MALI_DEBUG_ASSERT_POINTER(job); + + /* Remove the job from queue */ + _mali_osk_list_del(&job->list); + + /* Mark slot as busy */ + slot.state = MALI_GP_SLOT_STATE_WORKING; + + mali_gp_scheduler_unlock(); + + MALI_DEBUG_PRINT(3, ("Mali GP scheduler: Starting job %u (0x%08X)\n", mali_gp_job_get_id(job), job)); + + mali_group_start_gp_job(slot.group, job); + mali_group_unlock(slot.group); +} + +void mali_gp_scheduler_schedule(void) +{ + mali_group_lock(slot.group); + mali_gp_scheduler_lock(); + + mali_gp_scheduler_schedule_internal_and_unlock(); +} + +static void mali_gp_scheduler_return_job_to_user(struct mali_gp_job *job, mali_bool success) +{ + _mali_uk_gp_job_finished_s *jobres = job->finished_notification->result_buffer; + _mali_osk_memset(jobres, 0, sizeof(_mali_uk_gp_job_finished_s)); /* @@@@ can be removed once we initialize all members in this struct */ + jobres->user_job_ptr = mali_gp_job_get_user_id(job); + if (MALI_TRUE == success) { + jobres->status = _MALI_UK_JOB_STATUS_END_SUCCESS; + } else { + jobres->status = _MALI_UK_JOB_STATUS_END_UNKNOWN_ERR; + } + + jobres->heap_current_addr = mali_gp_job_get_current_heap_addr(job); + jobres->perf_counter0 = mali_gp_job_get_perf_counter_value0(job); + jobres->perf_counter1 = mali_gp_job_get_perf_counter_value1(job); + + mali_session_send_notification(mali_gp_job_get_session(job), job->finished_notification); + job->finished_notification = NULL; + + mali_gp_job_delete(job); + mali_gp_scheduler_job_completed(); +} + +/* Group must be locked when entering this function. Will be unlocked before exiting. */ +void mali_gp_scheduler_job_done(struct mali_group *group, struct mali_gp_job *job, mali_bool success) +{ + mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY; + + MALI_DEBUG_ASSERT_POINTER(group); + MALI_DEBUG_ASSERT_POINTER(job); + + MALI_DEBUG_ASSERT_LOCK_HELD(group->lock); + MALI_DEBUG_ASSERT(slot.group == group); + + MALI_DEBUG_PRINT(3, ("Mali GP scheduler: Job %u (0x%08X) completed (%s)\n", mali_gp_job_get_id(job), job, success ? "success" : "failure")); + + /* Release tracker. */ + schedule_mask |= mali_timeline_tracker_release(&job->tracker); + + /* Signal PP job. */ + schedule_mask |= mali_gp_job_signal_pp_tracker(job, success); + + mali_gp_scheduler_lock(); + + /* Mark slot as idle again */ + slot.state = MALI_GP_SLOT_STATE_IDLE; + + /* If paused, then this was the last job, so wake up sleeping workers */ + if (pause_count > 0) { + _mali_osk_wait_queue_wake_up(gp_scheduler_working_wait_queue); + } + + /* Schedule any queued GP jobs on this group. */ + mali_gp_scheduler_schedule_internal_and_unlock(); + + /* GP is now scheduled, removing it from the mask. */ + schedule_mask &= ~MALI_SCHEDULER_MASK_GP; + + if (MALI_SCHEDULER_MASK_EMPTY != schedule_mask) { + /* Releasing the tracker activated other jobs that need scheduling. */ + mali_scheduler_schedule_from_mask(schedule_mask, MALI_FALSE); + } + + /* Sends the job end message to user space and free the job object */ + mali_gp_scheduler_return_job_to_user(job, success); +} + +void mali_gp_scheduler_oom(struct mali_group *group, struct mali_gp_job *job) +{ + _mali_uk_gp_job_suspended_s * jobres; + _mali_osk_notification_t * notification; + + mali_gp_scheduler_lock(); + + notification = job->oom_notification; + job->oom_notification = NULL; + slot.returned_cookie = mali_gp_job_get_id(job); + + jobres = (_mali_uk_gp_job_suspended_s *)notification->result_buffer; + jobres->user_job_ptr = mali_gp_job_get_user_id(job); + jobres->cookie = mali_gp_job_get_id(job); + + mali_gp_scheduler_unlock(); + + mali_session_send_notification(mali_gp_job_get_session(job), notification); + + /* + * If this function failed, then we could return the job to user space right away, + * but there is a job timer anyway that will do that eventually. + * This is not exactly a common case anyway. + */ +} + +void mali_gp_scheduler_suspend(void) +{ + mali_gp_scheduler_lock(); + pause_count++; /* Increment the pause_count so that no more jobs will be scheduled */ + mali_gp_scheduler_unlock(); + + _mali_osk_wait_queue_wait_event(gp_scheduler_working_wait_queue, mali_gp_scheduler_is_suspended, NULL); +} + +void mali_gp_scheduler_resume(void) +{ + mali_gp_scheduler_lock(); + pause_count--; /* Decrement pause_count to allow scheduling again (if it reaches 0) */ + mali_gp_scheduler_unlock(); + if (0 == pause_count) { + mali_gp_scheduler_schedule(); + } +} + +mali_timeline_point mali_gp_scheduler_submit_job(struct mali_session_data *session, struct mali_gp_job *job) +{ + mali_timeline_point point; + + MALI_DEBUG_ASSERT_POINTER(session); + MALI_DEBUG_ASSERT_POINTER(job); + + mali_gp_scheduler_job_queued(); + + /* Add job to Timeline system. */ + point = mali_timeline_system_add_tracker(session->timeline_system, &job->tracker, MALI_TIMELINE_GP); + + return point; +} + +_mali_osk_errcode_t _mali_ukk_gp_start_job(void *ctx, _mali_uk_gp_start_job_s *uargs) +{ + struct mali_session_data *session; + struct mali_gp_job *job; + mali_timeline_point point; + u32 __user *timeline_point_ptr = NULL; + + MALI_DEBUG_ASSERT_POINTER(uargs); + MALI_DEBUG_ASSERT_POINTER(ctx); + + session = (struct mali_session_data*)ctx; + + job = mali_gp_job_create(session, uargs, mali_scheduler_get_new_id(), NULL); + if (NULL == job) { + MALI_PRINT_ERROR(("Failed to create GP job.\n")); + return _MALI_OSK_ERR_NOMEM; + } + + timeline_point_ptr = (u32 __user *) job->uargs.timeline_point_ptr; + + point = mali_gp_scheduler_submit_job(session, job); + + if (0 != _mali_osk_put_user(((u32) point), timeline_point_ptr)) { + /* Let user space know that something failed after the job was started. */ + return _MALI_OSK_ERR_ITEM_NOT_FOUND; + } + + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t _mali_ukk_get_gp_number_of_cores(_mali_uk_get_gp_number_of_cores_s *args) +{ + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + args->number_of_cores = 1; + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t _mali_ukk_get_gp_core_version(_mali_uk_get_gp_core_version_s *args) +{ + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + args->version = gp_version; + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t _mali_ukk_gp_suspend_response(_mali_uk_gp_suspend_response_s *args) +{ + struct mali_session_data *session; + struct mali_gp_job *resumed_job; + _mali_osk_notification_t *new_notification = 0; + + MALI_DEBUG_ASSERT_POINTER(args); + + if (NULL == args->ctx) { + return _MALI_OSK_ERR_INVALID_ARGS; + } + + session = (struct mali_session_data*)args->ctx; + if (NULL == session) { + return _MALI_OSK_ERR_FAULT; + } + + if (_MALIGP_JOB_RESUME_WITH_NEW_HEAP == args->code) { + new_notification = _mali_osk_notification_create(_MALI_NOTIFICATION_GP_STALLED, sizeof(_mali_uk_gp_job_suspended_s)); + + if (NULL == new_notification) { + MALI_PRINT_ERROR(("Mali GP scheduler: Failed to allocate notification object. Will abort GP job.\n")); + mali_group_lock(slot.group); + mali_group_abort_gp_job(slot.group, args->cookie); + mali_group_unlock(slot.group); + return _MALI_OSK_ERR_FAULT; + } + } + + mali_group_lock(slot.group); + + if (_MALIGP_JOB_RESUME_WITH_NEW_HEAP == args->code) { + MALI_DEBUG_PRINT(3, ("Mali GP scheduler: Resuming job %u with new heap; 0x%08X - 0x%08X\n", args->cookie, args->arguments[0], args->arguments[1])); + + resumed_job = mali_group_resume_gp_with_new_heap(slot.group, args->cookie, args->arguments[0], args->arguments[1]); + if (NULL != resumed_job) { + resumed_job->oom_notification = new_notification; + mali_group_unlock(slot.group); + return _MALI_OSK_ERR_OK; + } else { + mali_group_unlock(slot.group); + _mali_osk_notification_delete(new_notification); + return _MALI_OSK_ERR_FAULT; + } + } + + MALI_DEBUG_PRINT(2, ("Mali GP scheduler: Aborting job %u, no new heap provided\n", args->cookie)); + mali_group_abort_gp_job(slot.group, args->cookie); + mali_group_unlock(slot.group); + return _MALI_OSK_ERR_OK; +} + +void mali_gp_scheduler_abort_session(struct mali_session_data *session) +{ + struct mali_gp_job *job, *tmp; + _MALI_OSK_LIST_HEAD_STATIC_INIT(removed_jobs); + + MALI_DEBUG_ASSERT_POINTER(session); + MALI_DEBUG_ASSERT(session->is_aborting); + + MALI_DEBUG_PRINT(3, ("Mali GP scheduler: Aborting all jobs from session 0x%08X.\n", session)); + + mali_gp_scheduler_lock(); + + /* Find all jobs from the aborting session. */ + _MALI_OSK_LIST_FOREACHENTRY(job, tmp, &job_queue, struct mali_gp_job, list) { + if (job->session == session) { + MALI_DEBUG_PRINT(3, ("Mali GP scheduler: Removing job %u (0x%08X) from queue.\n", mali_gp_job_get_id(job), job)); + _mali_osk_list_move(&job->list, &removed_jobs); + } + } + + /* Find all high priority jobs from the aborting session. */ + _MALI_OSK_LIST_FOREACHENTRY(job, tmp, &job_queue_high, struct mali_gp_job, list) { + if (job->session == session) { + MALI_DEBUG_PRINT(3, ("Mali GP scheduler: Removing job %u (0x%08X) from queue.\n", mali_gp_job_get_id(job), job)); + _mali_osk_list_move(&job->list, &removed_jobs); + } + } + + mali_gp_scheduler_unlock(); + + /* Release and delete all found jobs from the aborting session. */ + _MALI_OSK_LIST_FOREACHENTRY(job, tmp, &removed_jobs, struct mali_gp_job, list) { + mali_timeline_tracker_release(&job->tracker); + mali_gp_job_signal_pp_tracker(job, MALI_FALSE); + mali_gp_job_delete(job); + mali_gp_scheduler_job_completed(); + } + + /* Abort any running jobs from the session. */ + mali_group_abort_session(slot.group, session); +} + +static mali_bool mali_gp_scheduler_is_suspended(void *data) +{ + mali_bool ret; + + /* This callback does not use the data pointer. */ + MALI_IGNORE(data); + + mali_gp_scheduler_lock(); + ret = pause_count > 0 && (slot.state == MALI_GP_SLOT_STATE_IDLE || slot.state == MALI_GP_SLOT_STATE_DISABLED); + mali_gp_scheduler_unlock(); + + return ret; +} + + +#if MALI_STATE_TRACKING +u32 mali_gp_scheduler_dump_state(char *buf, u32 size) +{ + int n = 0; + + n += _mali_osk_snprintf(buf + n, size - n, "GP\n"); + n += _mali_osk_snprintf(buf + n, size - n, "\tQueue is %s\n", _mali_osk_list_empty(&job_queue) ? "empty" : "not empty"); + n += _mali_osk_snprintf(buf + n, size - n, "\tHigh priority queue is %s\n", _mali_osk_list_empty(&job_queue_high) ? "empty" : "not empty"); + + n += mali_group_dump_state(slot.group, buf + n, size - n); + n += _mali_osk_snprintf(buf + n, size - n, "\n"); + + return n; +} +#endif + +void mali_gp_scheduler_reset_all_groups(void) +{ + if (NULL != slot.group) { + mali_group_lock(slot.group); + mali_group_reset(slot.group); + mali_group_unlock(slot.group); + } +} + +void mali_gp_scheduler_zap_all_active(struct mali_session_data *session) +{ + if (NULL != slot.group) { + mali_group_zap_session(slot.group, session); + } +} + +void mali_gp_scheduler_enable_group(struct mali_group *group) +{ + MALI_DEBUG_ASSERT_POINTER(group); + MALI_DEBUG_ASSERT(slot.group == group); + MALI_DEBUG_PRINT(2, ("Mali GP scheduler: enabling gp group %p\n", group)); + + mali_group_lock(group); + + if (MALI_GROUP_STATE_DISABLED != group->state) { + mali_group_unlock(group); + MALI_DEBUG_PRINT(2, ("Mali GP scheduler: gp group %p already enabled\n", group)); + return; + } + + mali_gp_scheduler_lock(); + + MALI_DEBUG_ASSERT(MALI_GROUP_STATE_DISABLED == group->state); + MALI_DEBUG_ASSERT(MALI_GP_SLOT_STATE_DISABLED == slot.state); + slot.state = MALI_GP_SLOT_STATE_IDLE; + group->state = MALI_GROUP_STATE_IDLE; + + mali_group_power_on_group(group); + mali_group_reset(group); + + /* Pick up any jobs that might have been queued while the GP group was disabled. */ + mali_gp_scheduler_schedule_internal_and_unlock(); +} + +void mali_gp_scheduler_disable_group(struct mali_group *group) +{ + MALI_DEBUG_ASSERT_POINTER(group); + MALI_DEBUG_ASSERT(slot.group == group); + MALI_DEBUG_PRINT(2, ("Mali GP scheduler: disabling gp group %p\n", group)); + + mali_gp_scheduler_suspend(); + mali_group_lock(group); + mali_gp_scheduler_lock(); + + MALI_DEBUG_ASSERT( MALI_GROUP_STATE_IDLE == group->state + || MALI_GROUP_STATE_DISABLED == group->state); + + if (MALI_GROUP_STATE_DISABLED == group->state) { + MALI_DEBUG_ASSERT(MALI_GP_SLOT_STATE_DISABLED == slot.state); + MALI_DEBUG_PRINT(2, ("Mali GP scheduler: gp group %p already disabled\n", group)); + } else { + MALI_DEBUG_ASSERT(MALI_GP_SLOT_STATE_IDLE == slot.state); + slot.state = MALI_GP_SLOT_STATE_DISABLED; + group->state = MALI_GROUP_STATE_DISABLED; + + mali_group_power_off_group(group, MALI_TRUE); + } + + mali_gp_scheduler_unlock(); + mali_group_unlock(group); + mali_gp_scheduler_resume(); +} + +static mali_scheduler_mask mali_gp_scheduler_queue_job(struct mali_gp_job *job) +{ + _mali_osk_list_t *queue = NULL; + mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY; + struct mali_gp_job *iter, *tmp; + + MALI_DEBUG_ASSERT_POINTER(job); + MALI_DEBUG_ASSERT_POINTER(job->session); + + MALI_DEBUG_ASSERT_LOCK_HELD(gp_scheduler_lock); + + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_SINGLE | MALI_PROFILING_EVENT_CHANNEL_SOFTWARE | MALI_PROFILING_EVENT_REASON_SINGLE_SW_GP_ENQUEUE, job->pid, job->tid, job->uargs.frame_builder_id, job->uargs.flush_id, 0); + + job->cache_order = mali_scheduler_get_new_cache_order(); + + /* Determine which queue the job should be added to. */ + if (job->session->use_high_priority_job_queue) { + queue = &job_queue_high; + } else { + queue = &job_queue; + } + + /* Find position in queue where job should be added. */ + _MALI_OSK_LIST_FOREACHENTRY_REVERSE(iter, tmp, queue, struct mali_gp_job, list) { + if (mali_gp_job_is_after(job, iter)) { + break; + } + } + + /* Add job to queue. */ + _mali_osk_list_add(&job->list, &iter->list); + + /* Set schedule bitmask if the GP core is idle. */ + if (MALI_GP_SLOT_STATE_IDLE == slot.state) { + schedule_mask |= MALI_SCHEDULER_MASK_GP; + } + +#if defined(CONFIG_GPU_TRACEPOINTS) && defined(CONFIG_TRACEPOINTS) + trace_gpu_job_enqueue(mali_gp_job_get_tid(job), mali_gp_job_get_id(job), "GP"); +#endif + + MALI_DEBUG_PRINT(3, ("Mali GP scheduler: Job %u (0x%08X) queued\n", mali_gp_job_get_id(job), job)); + + return schedule_mask; +} + +mali_scheduler_mask mali_gp_scheduler_activate_job(struct mali_gp_job *job) +{ + mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY; + + MALI_DEBUG_ASSERT_POINTER(job); + MALI_DEBUG_ASSERT_POINTER(job->session); + + MALI_DEBUG_PRINT(4, ("Mali GP scheduler: Timeline activation for job %u (0x%08X).\n", mali_gp_job_get_id(job), job)); + + mali_gp_scheduler_lock(); + + if (unlikely(job->session->is_aborting)) { + /* Before checking if the session is aborting, the scheduler must be locked. */ + MALI_DEBUG_ASSERT_LOCK_HELD(gp_scheduler_lock); + + MALI_DEBUG_PRINT(3, ("Mali GP scheduler: Job %u (0x%08X) activated while session is aborting.\n", mali_gp_job_get_id(job), job)); + + /* This job should not be on any list. */ + MALI_DEBUG_ASSERT(_mali_osk_list_empty(&job->list)); + + mali_gp_scheduler_unlock(); + + /* Release tracker and delete job. */ + mali_timeline_tracker_release(&job->tracker); + mali_gp_job_signal_pp_tracker(job, MALI_FALSE); + mali_gp_job_delete(job); + mali_gp_scheduler_job_completed(); + + /* Since we are aborting we ignore the scheduler mask. */ + return MALI_SCHEDULER_MASK_EMPTY; + } + + /* GP job is ready to run, queue it. */ + schedule_mask = mali_gp_scheduler_queue_job(job); + + mali_gp_scheduler_unlock(); + + return schedule_mask; +} + +static void mali_gp_scheduler_job_queued(void) +{ + /* We hold a PM reference for every job we hold queued (and running) */ + _mali_osk_pm_dev_ref_add(); + + if (mali_utilization_enabled()) { + /* + * We cheat a little bit by counting the PP as busy from the time a GP job is queued. + * This will be fine because we only loose the tiny idle gap between jobs, but + * we will instead get less utilization work to do (less locks taken) + */ + mali_utilization_gp_start(); + } +} + +static void mali_gp_scheduler_job_completed(void) +{ + /* Release the PM reference we got in the mali_gp_scheduler_job_queued() function */ + _mali_osk_pm_dev_ref_dec(); + + if (mali_utilization_enabled()) { + mali_utilization_gp_end(); + } +} diff --git a/drivers/gpu/arm/mali/common/mali_gp_scheduler.h b/drivers/gpu/arm/mali/common/mali_gp_scheduler.h new file mode 100644 index 00000000000000..8be9c47cb255da --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_gp_scheduler.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_GP_SCHEDULER_H__ +#define __MALI_GP_SCHEDULER_H__ + +#include "mali_osk.h" +#include "mali_gp_job.h" +#include "mali_group.h" + +_mali_osk_errcode_t mali_gp_scheduler_initialize(void); +void mali_gp_scheduler_terminate(void); + +void mali_gp_scheduler_job_done(struct mali_group *group, struct mali_gp_job *job, mali_bool success); +void mali_gp_scheduler_oom(struct mali_group *group, struct mali_gp_job *job); +u32 mali_gp_scheduler_dump_state(char *buf, u32 size); + +void mali_gp_scheduler_suspend(void); +void mali_gp_scheduler_resume(void); + +/** + * @brief Abort all running and queued GP jobs from session. + * +* This functions aborts all GP jobs from the specified session. Queued jobs are removed from the +* queue and jobs currently running on a core will be aborted. + * + * @param session Session that is aborting. + */ +void mali_gp_scheduler_abort_session(struct mali_session_data *session); + +/** + * @brief Reset all groups + * + * This function resets all groups known by the GP scheuduler. This must be + * called after the Mali HW has been powered on in order to reset the HW. + */ +void mali_gp_scheduler_reset_all_groups(void); + +/** + * @brief Zap TLB on all groups with \a session active + * + * The scheculer will zap the session on all groups it owns. + */ +void mali_gp_scheduler_zap_all_active(struct mali_session_data *session); + +/** + * @brief Re-enable a group that has been disabled with mali_gp_scheduler_disable_group + * + * If a Mali PMU is present, the group will be powered back on and added back + * into the GP scheduler. + * + * @param group Pointer to the group to enable + */ +void mali_gp_scheduler_enable_group(struct mali_group *group); + +/** + * @brief Disable a group + * + * The group will be taken out of the GP scheduler and powered off, if a Mali + * PMU is present. + * + * @param group Pointer to the group to disable + */ +void mali_gp_scheduler_disable_group(struct mali_group *group); + +/** + * @brief Used by the Timeline system to queue a GP job. + * + * @note @ref mali_scheduler_schedule_from_mask() should be called if this function returns non-zero. + * + * @param job The GP job that is being activated. + * + * @return A scheduling bitmask that can be used to decide if scheduling is necessary after this + * call. + */ +mali_scheduler_mask mali_gp_scheduler_activate_job(struct mali_gp_job *job); + +/** + * @brief Schedule queued jobs on idle cores. + */ +void mali_gp_scheduler_schedule(void); + +/** + * @brief Submit a GP job to the GP scheduler. + * + * This will add the GP job to the Timeline system. + * + * @param session Session this job belongs to. + * @param job GP job that will be submitted + * @return Point on GP timeline for job. + */ +mali_timeline_point mali_gp_scheduler_submit_job(struct mali_session_data *session, struct mali_gp_job *job); + +#endif /* __MALI_GP_SCHEDULER_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_group.c b/drivers/gpu/arm/mali/common/mali_group.c new file mode 100644 index 00000000000000..7383deacf1bf4a --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_group.c @@ -0,0 +1,1855 @@ +/* + * Copyright (C) 2011-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_group.h" +#include "mali_osk.h" +#include "mali_l2_cache.h" +#include "mali_gp.h" +#include "mali_pp.h" +#include "mali_mmu.h" +#include "mali_dlbu.h" +#include "mali_broadcast.h" +#include "mali_scheduler.h" +#include "mali_osk_profiling.h" +#include "mali_pm_domain.h" +#include "mali_pm.h" +#if defined(CONFIG_GPU_TRACEPOINTS) && defined(CONFIG_TRACEPOINTS) +#include +#include +#endif + + +static void mali_group_bottom_half_mmu(void *data); +static void mali_group_bottom_half_gp(void *data); +static void mali_group_bottom_half_pp(void *data); + +static void mali_group_timeout(void *data); +static void mali_group_reset_pp(struct mali_group *group); +static void mali_group_reset_mmu(struct mali_group *group); + +#if defined(CONFIG_MALI400_PROFILING) +static void mali_group_report_l2_cache_counters_per_core(struct mali_group *group, u32 core_num); +#endif /* #if defined(CONFIG_MALI400_PROFILING) */ + +/* + * The group object is the most important object in the device driver, + * and acts as the center of many HW operations. + * The reason for this is that operations on the MMU will affect all + * cores connected to this MMU (a group is defined by the MMU and the + * cores which are connected to this). + * The group lock is thus the most important lock, followed by the + * GP and PP scheduler locks. They must be taken in the following + * order: + * GP/PP lock first, then group lock(s). + */ + +static struct mali_group *mali_global_groups[MALI_MAX_NUMBER_OF_GROUPS] = { NULL, }; +static u32 mali_global_num_groups = 0; + +/* timer related */ +int mali_max_job_runtime = MALI_MAX_JOB_RUNTIME_DEFAULT; + +/* local helper functions */ +static void mali_group_activate_page_directory(struct mali_group *group, struct mali_session_data *session); +static void mali_group_remove_session_if_unused(struct mali_group *group, struct mali_session_data *session); +static void mali_group_recovery_reset(struct mali_group *group); +static void mali_group_mmu_page_fault_and_unlock(struct mali_group *group); + +static void mali_group_post_process_job_pp(struct mali_group *group); +static void mali_group_post_process_job_gp(struct mali_group *group, mali_bool suspend); + +void mali_group_lock(struct mali_group *group) +{ +#ifdef MALI_UPPER_HALF_SCHEDULING + _mali_osk_spinlock_irq_lock(group->lock); +#else + _mali_osk_spinlock_lock(group->lock); +#endif + MALI_DEBUG_PRINT(5, ("Mali group: Group lock taken 0x%08X\n", group)); +} + +void mali_group_unlock(struct mali_group *group) +{ + MALI_DEBUG_PRINT(5, ("Mali group: Releasing group lock 0x%08X\n", group)); +#ifdef MALI_UPPER_HALF_SCHEDULING + _mali_osk_spinlock_irq_unlock(group->lock); +#else + _mali_osk_spinlock_unlock(group->lock); +#endif +} + +#ifdef DEBUG +void mali_group_assert_locked(struct mali_group *group) +{ + MALI_DEBUG_ASSERT_LOCK_HELD(group->lock); +} +#endif + + +struct mali_group *mali_group_create(struct mali_l2_cache_core *core, struct mali_dlbu_core *dlbu, struct mali_bcast_unit *bcast) +{ + struct mali_group *group = NULL; + + if (mali_global_num_groups >= MALI_MAX_NUMBER_OF_GROUPS) { + MALI_PRINT_ERROR(("Mali group: Too many group objects created\n")); + return NULL; + } + + group = _mali_osk_calloc(1, sizeof(struct mali_group)); + if (NULL != group) { + group->timeout_timer = _mali_osk_timer_init(); + + if (NULL != group->timeout_timer) { + _mali_osk_lock_order_t order; + _mali_osk_timer_setcallback(group->timeout_timer, mali_group_timeout, (void *)group); + + if (NULL != dlbu) { + order = _MALI_OSK_LOCK_ORDER_GROUP_VIRTUAL; + } else { + order = _MALI_OSK_LOCK_ORDER_GROUP; + } + +#ifdef MALI_UPPER_HALF_SCHEDULING + group->lock = _mali_osk_spinlock_irq_init(_MALI_OSK_LOCKFLAG_ORDERED, order); +#else + group->lock = _mali_osk_spinlock_init(_MALI_OSK_LOCKFLAG_ORDERED, order); +#endif + + if (NULL != group->lock) { + group->l2_cache_core[0] = core; + group->session = NULL; + group->power_is_on = MALI_TRUE; + group->state = MALI_GROUP_STATE_IDLE; + _mali_osk_list_init(&group->group_list); + _mali_osk_list_init(&group->pp_scheduler_list); + group->parent_group = NULL; + group->l2_cache_core_ref_count[0] = 0; + group->l2_cache_core_ref_count[1] = 0; + group->bcast_core = bcast; + group->dlbu_core = dlbu; + + mali_global_groups[mali_global_num_groups] = group; + mali_global_num_groups++; + + return group; + } + _mali_osk_timer_term(group->timeout_timer); + } + _mali_osk_free(group); + } + + return NULL; +} + +_mali_osk_errcode_t mali_group_add_mmu_core(struct mali_group *group, struct mali_mmu_core* mmu_core) +{ + /* This group object now owns the MMU core object */ + group->mmu= mmu_core; + group->bottom_half_work_mmu = _mali_osk_wq_create_work(mali_group_bottom_half_mmu, group); + if (NULL == group->bottom_half_work_mmu) { + return _MALI_OSK_ERR_FAULT; + } + return _MALI_OSK_ERR_OK; +} + +void mali_group_remove_mmu_core(struct mali_group *group) +{ + /* This group object no longer owns the MMU core object */ + group->mmu = NULL; + if (NULL != group->bottom_half_work_mmu) { + _mali_osk_wq_delete_work(group->bottom_half_work_mmu); + } +} + +_mali_osk_errcode_t mali_group_add_gp_core(struct mali_group *group, struct mali_gp_core* gp_core) +{ + /* This group object now owns the GP core object */ + group->gp_core = gp_core; + group->bottom_half_work_gp = _mali_osk_wq_create_work(mali_group_bottom_half_gp, group); + if (NULL == group->bottom_half_work_gp) { + return _MALI_OSK_ERR_FAULT; + } + return _MALI_OSK_ERR_OK; +} + +void mali_group_remove_gp_core(struct mali_group *group) +{ + /* This group object no longer owns the GP core object */ + group->gp_core = NULL; + if (NULL != group->bottom_half_work_gp) { + _mali_osk_wq_delete_work(group->bottom_half_work_gp); + } +} + +_mali_osk_errcode_t mali_group_add_pp_core(struct mali_group *group, struct mali_pp_core* pp_core) +{ + /* This group object now owns the PP core object */ + group->pp_core = pp_core; + group->bottom_half_work_pp = _mali_osk_wq_create_work(mali_group_bottom_half_pp, group); + if (NULL == group->bottom_half_work_pp) { + return _MALI_OSK_ERR_FAULT; + } + return _MALI_OSK_ERR_OK; +} + +void mali_group_remove_pp_core(struct mali_group *group) +{ + /* This group object no longer owns the PP core object */ + group->pp_core = NULL; + if (NULL != group->bottom_half_work_pp) { + _mali_osk_wq_delete_work(group->bottom_half_work_pp); + } +} + +void mali_group_set_pm_domain(struct mali_group *group, struct mali_pm_domain *domain) +{ + group->pm_domain = domain; +} + +void mali_group_delete(struct mali_group *group) +{ + u32 i; + + MALI_DEBUG_PRINT(4, ("Deleting group %p\n", group)); + + MALI_DEBUG_ASSERT(NULL == group->parent_group); + + /* Delete the resources that this group owns */ + if (NULL != group->gp_core) { + mali_gp_delete(group->gp_core); + } + + if (NULL != group->pp_core) { + mali_pp_delete(group->pp_core); + } + + if (NULL != group->mmu) { + mali_mmu_delete(group->mmu); + } + + if (mali_group_is_virtual(group)) { + /* Remove all groups from virtual group */ + struct mali_group *child; + struct mali_group *temp; + + _MALI_OSK_LIST_FOREACHENTRY(child, temp, &group->group_list, struct mali_group, group_list) { + child->parent_group = NULL; + mali_group_delete(child); + } + + mali_dlbu_delete(group->dlbu_core); + + if (NULL != group->bcast_core) { + mali_bcast_unit_delete(group->bcast_core); + } + } + + for (i = 0; i < mali_global_num_groups; i++) { + if (mali_global_groups[i] == group) { + mali_global_groups[i] = NULL; + mali_global_num_groups--; + + if (i != mali_global_num_groups) { + /* We removed a group from the middle of the array -- move the last + * group to the current position to close the gap */ + mali_global_groups[i] = mali_global_groups[mali_global_num_groups]; + mali_global_groups[mali_global_num_groups] = NULL; + } + + break; + } + } + + if (NULL != group->timeout_timer) { + _mali_osk_timer_del(group->timeout_timer); + _mali_osk_timer_term(group->timeout_timer); + } + + if (NULL != group->bottom_half_work_mmu) { + _mali_osk_wq_delete_work(group->bottom_half_work_mmu); + } + + if (NULL != group->bottom_half_work_gp) { + _mali_osk_wq_delete_work(group->bottom_half_work_gp); + } + + if (NULL != group->bottom_half_work_pp) { + _mali_osk_wq_delete_work(group->bottom_half_work_pp); + } + +#ifdef MALI_UPPER_HALF_SCHEDULING + _mali_osk_spinlock_irq_term(group->lock); +#else + _mali_osk_spinlock_term(group->lock); +#endif + _mali_osk_free(group); +} + +MALI_DEBUG_CODE(static void mali_group_print_virtual(struct mali_group *vgroup) +{ + u32 i; + struct mali_group *group; + struct mali_group *temp; + + MALI_DEBUG_PRINT(4, ("Virtual group %p\n", vgroup)); + MALI_DEBUG_PRINT(4, ("l2_cache_core[0] = %p, ref = %d\n", vgroup->l2_cache_core[0], vgroup->l2_cache_core_ref_count[0])); + MALI_DEBUG_PRINT(4, ("l2_cache_core[1] = %p, ref = %d\n", vgroup->l2_cache_core[1], vgroup->l2_cache_core_ref_count[1])); + + i = 0; + _MALI_OSK_LIST_FOREACHENTRY(group, temp, &vgroup->group_list, struct mali_group, group_list) { + MALI_DEBUG_PRINT(4, ("[%d] %p, l2_cache_core[0] = %p\n", i, group, group->l2_cache_core[0])); + i++; + } +}) + +/** + * @brief Add child group to virtual group parent + * + * Before calling this function, child must have it's state set to JOINING_VIRTUAL + * to ensure it's not touched during the transition period. When this function returns, + * child's state will be IN_VIRTUAL. + */ +void mali_group_add_group(struct mali_group *parent, struct mali_group *child, mali_bool update_hw) +{ + mali_bool found; + u32 i; + struct mali_session_data *child_session; + + MALI_DEBUG_PRINT(3, ("Adding group %p to virtual group %p\n", child, parent)); + + MALI_ASSERT_GROUP_LOCKED(parent); + + MALI_DEBUG_ASSERT(mali_group_is_virtual(parent)); + MALI_DEBUG_ASSERT(!mali_group_is_virtual(child)); + MALI_DEBUG_ASSERT(NULL == child->parent_group); + MALI_DEBUG_ASSERT(MALI_GROUP_STATE_JOINING_VIRTUAL == child->state); + + _mali_osk_list_addtail(&child->group_list, &parent->group_list); + + child->state = MALI_GROUP_STATE_IN_VIRTUAL; + child->parent_group = parent; + + MALI_DEBUG_ASSERT_POINTER(child->l2_cache_core[0]); + + MALI_DEBUG_PRINT(4, ("parent->l2_cache_core: [0] = %p, [1] = %p\n", parent->l2_cache_core[0], parent->l2_cache_core[1])); + MALI_DEBUG_PRINT(4, ("child->l2_cache_core: [0] = %p, [1] = %p\n", child->l2_cache_core[0], child->l2_cache_core[1])); + + /* Keep track of the L2 cache cores of child groups */ + found = MALI_FALSE; + for (i = 0; i < 2; i++) { + if (parent->l2_cache_core[i] == child->l2_cache_core[0]) { + MALI_DEBUG_ASSERT(parent->l2_cache_core_ref_count[i] > 0); + parent->l2_cache_core_ref_count[i]++; + found = MALI_TRUE; + } + } + + if (!found) { + /* First time we see this L2 cache, add it to our list */ + i = (NULL == parent->l2_cache_core[0]) ? 0 : 1; + + MALI_DEBUG_PRINT(4, ("First time we see l2_cache %p. Adding to [%d] = %p\n", child->l2_cache_core[0], i, parent->l2_cache_core[i])); + + MALI_DEBUG_ASSERT(NULL == parent->l2_cache_core[i]); + + parent->l2_cache_core[i] = child->l2_cache_core[0]; + parent->l2_cache_core_ref_count[i]++; + } + + /* Update Broadcast Unit and DLBU */ + mali_bcast_add_group(parent->bcast_core, child); + mali_dlbu_add_group(parent->dlbu_core, child); + + child_session = child->session; + child->session = NULL; + + /* Above this comment, only software state is updated and the HW is not + * touched. Now, check if Mali is powered and skip the rest if it isn't + * powered. + */ + + if (!update_hw) { + MALI_DEBUG_CODE(mali_group_print_virtual(parent)); + return; + } + + /* Update MMU */ + if (parent->session == child_session) { + mali_mmu_zap_tlb(child->mmu); + } else { + if (NULL == parent->session) { + mali_mmu_activate_empty_page_directory(child->mmu); + } else { + mali_mmu_activate_page_directory(child->mmu, mali_session_get_page_directory(parent->session)); + } + } + + /* Update HW only if power is on */ + mali_bcast_reset(parent->bcast_core); + mali_dlbu_update_mask(parent->dlbu_core); + + /* Start job on child when parent is active */ + if (NULL != parent->pp_running_job) { + struct mali_pp_job *job = parent->pp_running_job; + MALI_DEBUG_PRINT(3, ("Group %x joining running job %d on virtual group %x\n", + child, mali_pp_job_get_id(job), parent)); + MALI_DEBUG_ASSERT(MALI_GROUP_STATE_WORKING == parent->state); + mali_pp_job_start(child->pp_core, job, mali_pp_core_get_id(child->pp_core), MALI_TRUE); + + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_SINGLE| + MALI_PROFILING_MAKE_EVENT_CHANNEL_PP(mali_pp_core_get_id(child->pp_core))| + MALI_PROFILING_EVENT_REASON_SINGLE_HW_FLUSH, + mali_pp_job_get_frame_builder_id(job), mali_pp_job_get_flush_id(job), 0, 0, 0); + + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_START| + MALI_PROFILING_MAKE_EVENT_CHANNEL_PP(mali_pp_core_get_id(child->pp_core))| + MALI_PROFILING_EVENT_REASON_START_STOP_HW_VIRTUAL, + mali_pp_job_get_pid(job), mali_pp_job_get_tid(job), 0, 0, 0); + } + + MALI_DEBUG_CODE(mali_group_print_virtual(parent);) +} + +/** + * @brief Remove child group from virtual group parent + * + * After the child is removed, it's state will be LEAVING_VIRTUAL and must be set + * to IDLE before it can be used. + */ +void mali_group_remove_group(struct mali_group *parent, struct mali_group *child) +{ + u32 i; + + MALI_ASSERT_GROUP_LOCKED(parent); + + MALI_DEBUG_PRINT(3, ("Removing group %p from virtual group %p\n", child, parent)); + + MALI_DEBUG_ASSERT(mali_group_is_virtual(parent)); + MALI_DEBUG_ASSERT(!mali_group_is_virtual(child)); + MALI_DEBUG_ASSERT(parent == child->parent_group); + MALI_DEBUG_ASSERT(MALI_GROUP_STATE_IN_VIRTUAL == child->state); + /* Removing groups while running is not yet supported. */ + MALI_DEBUG_ASSERT(MALI_GROUP_STATE_IDLE == parent->state); + + mali_group_lock(child); + + /* Update Broadcast Unit and DLBU */ + mali_bcast_remove_group(parent->bcast_core, child); + mali_dlbu_remove_group(parent->dlbu_core, child); + + /* Update HW only if power is on */ + if (mali_pm_is_power_on()) { + mali_bcast_reset(parent->bcast_core); + mali_dlbu_update_mask(parent->dlbu_core); + } + + _mali_osk_list_delinit(&child->group_list); + + child->session = parent->session; + child->parent_group = NULL; + child->state = MALI_GROUP_STATE_LEAVING_VIRTUAL; + + /* Keep track of the L2 cache cores of child groups */ + i = (child->l2_cache_core[0] == parent->l2_cache_core[0]) ? 0 : 1; + + MALI_DEBUG_ASSERT(child->l2_cache_core[0] == parent->l2_cache_core[i]); + + parent->l2_cache_core_ref_count[i]--; + + if (parent->l2_cache_core_ref_count[i] == 0) { + parent->l2_cache_core[i] = NULL; + } + + MALI_DEBUG_CODE(mali_group_print_virtual(parent)); + + mali_group_unlock(child); +} + +struct mali_group *mali_group_acquire_group(struct mali_group *parent) +{ + struct mali_group *child; + + MALI_ASSERT_GROUP_LOCKED(parent); + + MALI_DEBUG_ASSERT(mali_group_is_virtual(parent)); + MALI_DEBUG_ASSERT(!_mali_osk_list_empty(&parent->group_list)); + + child = _MALI_OSK_LIST_ENTRY(parent->group_list.prev, struct mali_group, group_list); + + mali_group_remove_group(parent, child); + + return child; +} + +void mali_group_reset(struct mali_group *group) +{ + /* + * This function should not be used to abort jobs, + * currently only called during insmod and PM resume + */ + MALI_DEBUG_ASSERT_LOCK_HELD(group->lock); + MALI_DEBUG_ASSERT(NULL == group->gp_running_job); + MALI_DEBUG_ASSERT(NULL == group->pp_running_job); + + group->session = NULL; + + if (NULL != group->dlbu_core) { + mali_dlbu_reset(group->dlbu_core); + } + + if (NULL != group->bcast_core) { + mali_bcast_reset(group->bcast_core); + } + + if (NULL != group->mmu) { + mali_group_reset_mmu(group); + } + + if (NULL != group->gp_core) { + mali_gp_reset(group->gp_core); + } + + if (NULL != group->pp_core) { + mali_group_reset_pp(group); + } +} + +struct mali_gp_core* mali_group_get_gp_core(struct mali_group *group) +{ + return group->gp_core; +} + +struct mali_pp_core* mali_group_get_pp_core(struct mali_group *group) +{ + return group->pp_core; +} + +void mali_group_start_gp_job(struct mali_group *group, struct mali_gp_job *job) +{ + struct mali_session_data *session; + + MALI_ASSERT_GROUP_LOCKED(group); + MALI_DEBUG_ASSERT(MALI_GROUP_STATE_IDLE == group->state); + + session = mali_gp_job_get_session(job); + + if (NULL != group->l2_cache_core[0]) { + mali_l2_cache_invalidate_conditional(group->l2_cache_core[0], mali_gp_job_get_cache_order(job)); + } + + mali_group_activate_page_directory(group, session); + + mali_gp_job_start(group->gp_core, job); + + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_SINGLE | + MALI_PROFILING_MAKE_EVENT_CHANNEL_GP(0) | + MALI_PROFILING_EVENT_REASON_SINGLE_HW_FLUSH, + mali_gp_job_get_frame_builder_id(job), mali_gp_job_get_flush_id(job), 0, 0, 0); + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_START | + MALI_PROFILING_MAKE_EVENT_CHANNEL_GP(0), + mali_gp_job_get_pid(job), mali_gp_job_get_tid(job), 0, 0, 0); +#if defined(CONFIG_MALI400_PROFILING) + if ((MALI_HW_CORE_NO_COUNTER != mali_l2_cache_core_get_counter_src0(group->l2_cache_core[0])) && + (MALI_HW_CORE_NO_COUNTER != mali_l2_cache_core_get_counter_src1(group->l2_cache_core[0]))) + mali_group_report_l2_cache_counters_per_core(group, 0); +#endif /* #if defined(CONFIG_MALI400_PROFILING) */ + +#if defined(CONFIG_GPU_TRACEPOINTS) && defined(CONFIG_TRACEPOINTS) + trace_gpu_sched_switch(mali_gp_get_hw_core_desc(group->gp_core), sched_clock(), + mali_gp_job_get_pid(job), 0, mali_gp_job_get_id(job)); +#endif + + group->gp_running_job = job; + group->state = MALI_GROUP_STATE_WORKING; + + /* Setup the timeout timer value and save the job id for the job running on the gp core */ + _mali_osk_timer_mod(group->timeout_timer, _mali_osk_time_mstoticks(mali_max_job_runtime)); +} + +void mali_group_start_pp_job(struct mali_group *group, struct mali_pp_job *job, u32 sub_job) +{ + struct mali_session_data *session; + + MALI_ASSERT_GROUP_LOCKED(group); + MALI_DEBUG_ASSERT(MALI_GROUP_STATE_IDLE == group->state); + + session = mali_pp_job_get_session(job); + + if (NULL != group->l2_cache_core[0]) { + mali_l2_cache_invalidate_conditional(group->l2_cache_core[0], mali_pp_job_get_cache_order(job)); + } + + if (NULL != group->l2_cache_core[1]) { + mali_l2_cache_invalidate_conditional(group->l2_cache_core[1], mali_pp_job_get_cache_order(job)); + } + + mali_group_activate_page_directory(group, session); + + if (mali_group_is_virtual(group)) { + struct mali_group *child; + struct mali_group *temp; + u32 core_num = 0; + + MALI_DEBUG_ASSERT( mali_pp_job_is_virtual(job)); + + /* Configure DLBU for the job */ + mali_dlbu_config_job(group->dlbu_core, job); + + /* Write stack address for each child group */ + _MALI_OSK_LIST_FOREACHENTRY(child, temp, &group->group_list, struct mali_group, group_list) { + mali_pp_write_addr_stack(child->pp_core, job); + core_num++; + } + + /* Try to use DMA unit to start job, fallback to writing directly to the core */ + MALI_DEBUG_ASSERT(mali_dma_cmd_buf_is_valid(&job->dma_cmd_buf)); + if (_MALI_OSK_ERR_OK != mali_dma_start(mali_dma_get_global_dma_core(), &job->dma_cmd_buf)) { + mali_pp_job_start(group->pp_core, job, sub_job, MALI_FALSE); + } + } else { + mali_pp_job_start(group->pp_core, job, sub_job, MALI_FALSE); + } + + /* if the group is virtual, loop through physical groups which belong to this group + * and call profiling events for its cores as virtual */ + if (MALI_TRUE == mali_group_is_virtual(group)) { + struct mali_group *child; + struct mali_group *temp; + + _MALI_OSK_LIST_FOREACHENTRY(child, temp, &group->group_list, struct mali_group, group_list) { + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_SINGLE| + MALI_PROFILING_MAKE_EVENT_CHANNEL_PP(mali_pp_core_get_id(child->pp_core))| + MALI_PROFILING_EVENT_REASON_SINGLE_HW_FLUSH, + mali_pp_job_get_frame_builder_id(job), mali_pp_job_get_flush_id(job), 0, 0, 0); + + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_START| + MALI_PROFILING_MAKE_EVENT_CHANNEL_PP(mali_pp_core_get_id(child->pp_core))| + MALI_PROFILING_EVENT_REASON_START_STOP_HW_VIRTUAL, + mali_pp_job_get_pid(job), mali_pp_job_get_tid(job), 0, 0, 0); + } +#if defined(CONFIG_MALI400_PROFILING) + if (0 != group->l2_cache_core_ref_count[0]) { + if ((MALI_HW_CORE_NO_COUNTER != mali_l2_cache_core_get_counter_src0(group->l2_cache_core[0])) && + (MALI_HW_CORE_NO_COUNTER != mali_l2_cache_core_get_counter_src1(group->l2_cache_core[0]))) { + mali_group_report_l2_cache_counters_per_core(group, mali_l2_cache_get_id(group->l2_cache_core[0])); + } + } + if (0 != group->l2_cache_core_ref_count[1]) { + if ((MALI_HW_CORE_NO_COUNTER != mali_l2_cache_core_get_counter_src0(group->l2_cache_core[1])) && + (MALI_HW_CORE_NO_COUNTER != mali_l2_cache_core_get_counter_src1(group->l2_cache_core[1]))) { + mali_group_report_l2_cache_counters_per_core(group, mali_l2_cache_get_id(group->l2_cache_core[1])); + } + } +#endif /* #if defined(CONFIG_MALI400_PROFILING) */ + } else { /* group is physical - call profiling events for physical cores */ + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_SINGLE| + MALI_PROFILING_MAKE_EVENT_CHANNEL_PP(mali_pp_core_get_id(group->pp_core))| + MALI_PROFILING_EVENT_REASON_SINGLE_HW_FLUSH, + mali_pp_job_get_frame_builder_id(job), mali_pp_job_get_flush_id(job), 0, 0, 0); + + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_START| + MALI_PROFILING_MAKE_EVENT_CHANNEL_PP(mali_pp_core_get_id(group->pp_core))| + MALI_PROFILING_EVENT_REASON_START_STOP_HW_PHYSICAL, + mali_pp_job_get_pid(job), mali_pp_job_get_tid(job), 0, 0, 0); +#if defined(CONFIG_MALI400_PROFILING) + if ((MALI_HW_CORE_NO_COUNTER != mali_l2_cache_core_get_counter_src0(group->l2_cache_core[0])) && + (MALI_HW_CORE_NO_COUNTER != mali_l2_cache_core_get_counter_src1(group->l2_cache_core[0]))) { + mali_group_report_l2_cache_counters_per_core(group, mali_l2_cache_get_id(group->l2_cache_core[0])); + } +#endif /* #if defined(CONFIG_MALI400_PROFILING) */ + } +#if defined(CONFIG_GPU_TRACEPOINTS) && defined(CONFIG_TRACEPOINTS) + trace_gpu_sched_switch(mali_pp_get_hw_core_desc(group->pp_core), sched_clock(), mali_pp_job_get_tid(job), 0, mali_pp_job_get_id(job)); +#endif + group->pp_running_job = job; + group->pp_running_sub_job = sub_job; + group->state = MALI_GROUP_STATE_WORKING; + + /* Setup the timeout timer value and save the job id for the job running on the pp core */ + _mali_osk_timer_mod(group->timeout_timer, _mali_osk_time_mstoticks(mali_max_job_runtime)); +} + +struct mali_gp_job *mali_group_resume_gp_with_new_heap(struct mali_group *group, u32 job_id, u32 start_addr, u32 end_addr) +{ + MALI_ASSERT_GROUP_LOCKED(group); + + if (group->state != MALI_GROUP_STATE_OOM || + mali_gp_job_get_id(group->gp_running_job) != job_id) { + return NULL; /* Illegal request or job has already been aborted */ + } + + if (NULL != group->l2_cache_core[0]) { + mali_l2_cache_invalidate(group->l2_cache_core[0]); + } + + mali_mmu_zap_tlb_without_stall(group->mmu); + + mali_gp_resume_with_new_heap(group->gp_core, start_addr, end_addr); + + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_RESUME|MALI_PROFILING_MAKE_EVENT_CHANNEL_GP(0), 0, 0, 0, 0, 0); + + group->state = MALI_GROUP_STATE_WORKING; + + return group->gp_running_job; +} + +static void mali_group_reset_mmu(struct mali_group *group) +{ + struct mali_group *child; + struct mali_group *temp; + _mali_osk_errcode_t err; + + if (!mali_group_is_virtual(group)) { + /* This is a physical group or an idle virtual group -- simply wait for + * the reset to complete. */ + err = mali_mmu_reset(group->mmu); + MALI_DEBUG_ASSERT(_MALI_OSK_ERR_OK == err); + } else { /* virtual group */ + err = mali_mmu_reset(group->mmu); + if (_MALI_OSK_ERR_OK == err) { + return; + } + + /* Loop through all members of this virtual group and wait + * until they are done resetting. + */ + _MALI_OSK_LIST_FOREACHENTRY(child, temp, &group->group_list, struct mali_group, group_list) { + err = mali_mmu_reset(child->mmu); + MALI_DEBUG_ASSERT(_MALI_OSK_ERR_OK == err); + } + } +} + +static void mali_group_reset_pp(struct mali_group *group) +{ + struct mali_group *child; + struct mali_group *temp; + + mali_pp_reset_async(group->pp_core); + + if (!mali_group_is_virtual(group) || NULL == group->pp_running_job) { + /* This is a physical group or an idle virtual group -- simply wait for + * the reset to complete. */ + mali_pp_reset_wait(group->pp_core); + } else { /* virtual group */ + /* Loop through all members of this virtual group and wait until they + * are done resetting. + */ + _MALI_OSK_LIST_FOREACHENTRY(child, temp, &group->group_list, struct mali_group, group_list) { + mali_pp_reset_wait(child->pp_core); + } + } +} + +/* Group must be locked when entering this function. Will be unlocked before exiting. */ +static void mali_group_complete_pp_and_unlock(struct mali_group *group, mali_bool success, mali_bool in_upper_half) +{ + struct mali_pp_job *pp_job_to_return; + u32 pp_sub_job_to_return; + + MALI_DEBUG_ASSERT_POINTER(group); + MALI_DEBUG_ASSERT_POINTER(group->pp_core); + MALI_DEBUG_ASSERT_POINTER(group->pp_running_job); + MALI_ASSERT_GROUP_LOCKED(group); + + mali_group_post_process_job_pp(group); + + if (success) { + /* Only do soft reset for successful jobs, a full recovery + * reset will be done for failed jobs. */ + mali_pp_reset_async(group->pp_core); + } + + pp_job_to_return = group->pp_running_job; + pp_sub_job_to_return = group->pp_running_sub_job; + group->state = MALI_GROUP_STATE_IDLE; + group->pp_running_job = NULL; + + if (!success) { + MALI_DEBUG_PRINT(2, ("Mali group: Executing recovery reset due to job failure\n")); + mali_group_recovery_reset(group); + } else if (_MALI_OSK_ERR_OK != mali_pp_reset_wait(group->pp_core)) { + MALI_PRINT_ERROR(("Mali group: Executing recovery reset due to reset failure\n")); + mali_group_recovery_reset(group); + } + + /* Return job to user, schedule and unlock group. */ + mali_pp_scheduler_job_done(group, pp_job_to_return, pp_sub_job_to_return, success, in_upper_half); +} + +/* Group must be locked when entering this function. Will be unlocked before exiting. */ +static void mali_group_complete_gp_and_unlock(struct mali_group *group, mali_bool success) +{ + struct mali_gp_job *gp_job_to_return; + + MALI_DEBUG_ASSERT_POINTER(group); + MALI_DEBUG_ASSERT_POINTER(group->gp_core); + MALI_DEBUG_ASSERT_POINTER(group->gp_running_job); + MALI_ASSERT_GROUP_LOCKED(group); + + mali_group_post_process_job_gp(group, MALI_FALSE); + + if (success) { + /* Only do soft reset for successful jobs, a full recovery + * reset will be done for failed jobs. */ + mali_gp_reset_async(group->gp_core); + } + + gp_job_to_return = group->gp_running_job; + group->state = MALI_GROUP_STATE_IDLE; + group->gp_running_job = NULL; + + if (!success) { + MALI_DEBUG_PRINT(2, ("Mali group: Executing recovery reset due to job failure\n")); + mali_group_recovery_reset(group); + } else if (_MALI_OSK_ERR_OK != mali_gp_reset_wait(group->gp_core)) { + MALI_PRINT_ERROR(("Mali group: Executing recovery reset due to reset failure\n")); + mali_group_recovery_reset(group); + } + + /* Return job to user, schedule and unlock group. */ + mali_gp_scheduler_job_done(group, gp_job_to_return, success); +} + +void mali_group_abort_gp_job(struct mali_group *group, u32 job_id) +{ + MALI_ASSERT_GROUP_LOCKED(group); + + if (MALI_GROUP_STATE_IDLE == group->state || + mali_gp_job_get_id(group->gp_running_job) != job_id) { + return; /* No need to cancel or job has already been aborted or completed */ + } + + /* Function will unlock the group, so we need to lock it again */ + mali_group_complete_gp_and_unlock(group, MALI_FALSE); + mali_group_lock(group); +} + +static void mali_group_abort_pp_job(struct mali_group *group, u32 job_id) +{ + MALI_ASSERT_GROUP_LOCKED(group); + + if (MALI_GROUP_STATE_IDLE == group->state || + mali_pp_job_get_id(group->pp_running_job) != job_id) { + return; /* No need to cancel or job has already been aborted or completed */ + } + + mali_group_complete_pp_and_unlock(group, MALI_FALSE, MALI_FALSE); + mali_group_lock(group); +} + +void mali_group_abort_session(struct mali_group *group, struct mali_session_data *session) +{ + struct mali_gp_job *gp_job; + struct mali_pp_job *pp_job; + u32 gp_job_id = 0; + u32 pp_job_id = 0; + mali_bool abort_pp = MALI_FALSE; + mali_bool abort_gp = MALI_FALSE; + + mali_group_lock(group); + + if (mali_group_is_in_virtual(group)) { + /* Group is member of a virtual group, don't touch it! */ + mali_group_unlock(group); + return; + } + + gp_job = group->gp_running_job; + pp_job = group->pp_running_job; + + if ((NULL != gp_job) && (mali_gp_job_get_session(gp_job) == session)) { + MALI_DEBUG_PRINT(4, ("Aborting GP job 0x%08x from session 0x%08x\n", gp_job, session)); + + gp_job_id = mali_gp_job_get_id(gp_job); + abort_gp = MALI_TRUE; + } + + if ((NULL != pp_job) && (mali_pp_job_get_session(pp_job) == session)) { + MALI_DEBUG_PRINT(4, ("Mali group: Aborting PP job 0x%08x from session 0x%08x\n", pp_job, session)); + + pp_job_id = mali_pp_job_get_id(pp_job); + abort_pp = MALI_TRUE; + } + + if (abort_gp) { + mali_group_abort_gp_job(group, gp_job_id); + } + if (abort_pp) { + mali_group_abort_pp_job(group, pp_job_id); + } + + mali_group_remove_session_if_unused(group, session); + + mali_group_unlock(group); +} + +struct mali_group *mali_group_get_glob_group(u32 index) +{ + if(mali_global_num_groups > index) { + return mali_global_groups[index]; + } + + return NULL; +} + +u32 mali_group_get_glob_num_groups(void) +{ + return mali_global_num_groups; +} + +static void mali_group_activate_page_directory(struct mali_group *group, struct mali_session_data *session) +{ + MALI_ASSERT_GROUP_LOCKED(group); + + MALI_DEBUG_PRINT(5, ("Mali group: Activating page directory 0x%08X from session 0x%08X on group 0x%08X\n", mali_session_get_page_directory(session), session, group)); + + if (group->session != session) { + /* Different session than last time, so we need to do some work */ + MALI_DEBUG_PRINT(5, ("Mali group: Activate session: %08x previous: %08x on group 0x%08X\n", session, group->session, group)); + mali_mmu_activate_page_directory(group->mmu, mali_session_get_page_directory(session)); + group->session = session; + } else { + /* Same session as last time, so no work required */ + MALI_DEBUG_PRINT(4, ("Mali group: Activate existing session 0x%08X on group 0x%08X\n", session->page_directory, group)); + mali_mmu_zap_tlb_without_stall(group->mmu); + } +} + +static void mali_group_remove_session_if_unused(struct mali_group *group, struct mali_session_data *session) +{ + MALI_ASSERT_GROUP_LOCKED(group); + + if (MALI_GROUP_STATE_IDLE == group->state) { + if (group->session == session) { + MALI_DEBUG_ASSERT(MALI_GROUP_STATE_WORKING != group->state); + MALI_DEBUG_ASSERT(MALI_TRUE == group->power_is_on); + MALI_DEBUG_PRINT(3, ("Mali group: Deactivating unused session 0x%08X on group %08X\n", session, group)); + mali_mmu_activate_empty_page_directory(group->mmu); + group->session = NULL; + } + } +} + +mali_bool mali_group_power_is_on(struct mali_group *group) +{ + MALI_DEBUG_ASSERT_LOCK_HELD(group->lock); + return group->power_is_on; +} + +void mali_group_power_on_group(struct mali_group *group) +{ + MALI_DEBUG_ASSERT_POINTER(group); + MALI_DEBUG_ASSERT_LOCK_HELD(group->lock); + MALI_DEBUG_ASSERT( MALI_GROUP_STATE_IDLE == group->state + || MALI_GROUP_STATE_IN_VIRTUAL == group->state + || MALI_GROUP_STATE_JOINING_VIRTUAL == group->state + || MALI_GROUP_STATE_LEAVING_VIRTUAL == group->state + || MALI_GROUP_STATE_DISABLED == group->state); + + MALI_DEBUG_PRINT(3, ("Group %p powered on\n", group)); + + group->power_is_on = MALI_TRUE; +} + +void mali_group_power_off_group(struct mali_group *group, mali_bool do_power_change) +{ + MALI_DEBUG_ASSERT_POINTER(group); + MALI_DEBUG_ASSERT_LOCK_HELD(group->lock); + MALI_DEBUG_ASSERT( MALI_GROUP_STATE_IDLE == group->state + || MALI_GROUP_STATE_IN_VIRTUAL == group->state + || MALI_GROUP_STATE_JOINING_VIRTUAL == group->state + || MALI_GROUP_STATE_LEAVING_VIRTUAL == group->state + || MALI_GROUP_STATE_DISABLED == group->state); + + MALI_DEBUG_PRINT(3, ("Group %p powered off\n", group)); + + /* It is necessary to set group->session = NULL so that the powered off MMU is not written + * to on map/unmap. It is also necessary to set group->power_is_on = MALI_FALSE so that + * pending bottom_halves does not access powered off cores. */ + + group->session = NULL; + + if (do_power_change) { + group->power_is_on = MALI_FALSE; + } +} + +void mali_group_power_on(void) +{ + int i; + for (i = 0; i < mali_global_num_groups; i++) { + struct mali_group *group = mali_global_groups[i]; + + mali_group_lock(group); + if (MALI_GROUP_STATE_DISABLED == group->state) { + MALI_DEBUG_ASSERT(MALI_FALSE == group->power_is_on); + } else { + mali_group_power_on_group(group); + } + mali_group_unlock(group); + } + MALI_DEBUG_PRINT(4, ("Mali Group: power on\n")); +} + +void mali_group_power_off(mali_bool do_power_change) +{ + int i; + + for (i = 0; i < mali_global_num_groups; i++) { + struct mali_group *group = mali_global_groups[i]; + + mali_group_lock(group); + if (MALI_GROUP_STATE_DISABLED == group->state) { + MALI_DEBUG_ASSERT(MALI_FALSE == group->power_is_on); + } else { + mali_group_power_off_group(group, do_power_change); + } + mali_group_unlock(group); + } + MALI_DEBUG_PRINT(4, ("Mali Group: power off\n")); +} + +static void mali_group_recovery_reset(struct mali_group *group) +{ + _mali_osk_errcode_t err; + + MALI_ASSERT_GROUP_LOCKED(group); + + /* Stop cores, bus stop */ + if (NULL != group->pp_core) { + mali_pp_stop_bus(group->pp_core); + } else { + mali_gp_stop_bus(group->gp_core); + } + + /* Flush MMU and clear page fault (if any) */ + mali_mmu_activate_fault_flush_page_directory(group->mmu); + mali_mmu_page_fault_done(group->mmu); + + /* Wait for cores to stop bus, then do a hard reset on them */ + if (NULL != group->pp_core) { + if (mali_group_is_virtual(group)) { + struct mali_group *child, *temp; + + /* Disable the broadcast unit while we do reset directly on the member cores. */ + mali_bcast_disable(group->bcast_core); + + _MALI_OSK_LIST_FOREACHENTRY(child, temp, &group->group_list, struct mali_group, group_list) { + mali_pp_stop_bus_wait(child->pp_core); + mali_pp_hard_reset(child->pp_core); + } + + mali_bcast_enable(group->bcast_core); + } else { + mali_pp_stop_bus_wait(group->pp_core); + mali_pp_hard_reset(group->pp_core); + } + } else { + mali_gp_stop_bus_wait(group->gp_core); + mali_gp_hard_reset(group->gp_core); + } + + /* Reset MMU */ + err = mali_mmu_reset(group->mmu); + MALI_DEBUG_ASSERT(_MALI_OSK_ERR_OK == err); + MALI_IGNORE(err); + + group->session = NULL; +} + +#if MALI_STATE_TRACKING +u32 mali_group_dump_state(struct mali_group *group, char *buf, u32 size) +{ + int n = 0; + + n += _mali_osk_snprintf(buf + n, size - n, "Group: %p\n", group); + n += _mali_osk_snprintf(buf + n, size - n, "\tstate: %d\n", group->state); + if (group->gp_core) { + n += mali_gp_dump_state(group->gp_core, buf + n, size - n); + n += _mali_osk_snprintf(buf + n, size - n, "\tGP job: %p\n", group->gp_running_job); + } + if (group->pp_core) { + n += mali_pp_dump_state(group->pp_core, buf + n, size - n); + n += _mali_osk_snprintf(buf + n, size - n, "\tPP job: %p, subjob %d \n", + group->pp_running_job, group->pp_running_sub_job); + } + + return n; +} +#endif + +/* Group must be locked when entering this function. Will be unlocked before exiting. */ +static void mali_group_mmu_page_fault_and_unlock(struct mali_group *group) +{ + MALI_DEBUG_ASSERT_POINTER(group); + MALI_ASSERT_GROUP_LOCKED(group); + + if (NULL != group->pp_core) { + struct mali_pp_job *pp_job_to_return; + u32 pp_sub_job_to_return; + + MALI_DEBUG_ASSERT_POINTER(group->pp_running_job); + + mali_group_post_process_job_pp(group); + + pp_job_to_return = group->pp_running_job; + pp_sub_job_to_return = group->pp_running_sub_job; + group->state = MALI_GROUP_STATE_IDLE; + group->pp_running_job = NULL; + + mali_group_recovery_reset(group); /* This will also clear the page fault itself */ + + /* Will unlock group. */ + mali_pp_scheduler_job_done(group, pp_job_to_return, pp_sub_job_to_return, MALI_FALSE, MALI_FALSE); + } else { + struct mali_gp_job *gp_job_to_return; + + MALI_DEBUG_ASSERT_POINTER(group->gp_running_job); + + mali_group_post_process_job_gp(group, MALI_FALSE); + + gp_job_to_return = group->gp_running_job; + group->state = MALI_GROUP_STATE_IDLE; + group->gp_running_job = NULL; + + mali_group_recovery_reset(group); /* This will also clear the page fault itself */ + + /* Will unlock group. */ + mali_gp_scheduler_job_done(group, gp_job_to_return, MALI_FALSE); + } +} + +_mali_osk_errcode_t mali_group_upper_half_mmu(void * data) +{ + _mali_osk_errcode_t err = _MALI_OSK_ERR_FAULT; + struct mali_group *group = (struct mali_group *)data; + struct mali_mmu_core *mmu = group->mmu; + u32 int_stat; + + MALI_DEBUG_ASSERT_POINTER(mmu); + +#if defined(CONFIG_MALI_SHARED_INTERRUPTS) + if (MALI_FALSE == mali_pm_domain_lock_state(group->pm_domain)) { + goto out; + } +#endif + + /* Check if it was our device which caused the interrupt (we could be sharing the IRQ line) */ + int_stat = mali_mmu_get_int_status(mmu); + if (0 != int_stat) { + struct mali_group *parent = group->parent_group; + + /* page fault or bus error, we thread them both in the same way */ + mali_mmu_mask_all_interrupts(mmu); + if (NULL == parent) { + _mali_osk_wq_schedule_work(group->bottom_half_work_mmu); + } else { + _mali_osk_wq_schedule_work(parent->bottom_half_work_mmu); + } + err = _MALI_OSK_ERR_OK; + goto out; + } + +out: +#if defined(CONFIG_MALI_SHARED_INTERRUPTS) + mali_pm_domain_unlock_state(group->pm_domain); +#endif + + return err; +} + +static void mali_group_bottom_half_mmu(void * data) +{ + struct mali_group *group = (struct mali_group *)data; + struct mali_mmu_core *mmu = group->mmu; + u32 rawstat; + MALI_DEBUG_CODE(u32 status); + + MALI_DEBUG_ASSERT_POINTER(mmu); + + mali_group_lock(group); + + MALI_DEBUG_ASSERT(NULL == group->parent_group); + + if ( MALI_FALSE == mali_group_power_is_on(group) ) { + MALI_PRINT_ERROR(("Interrupt bottom half of %s when core is OFF.", mmu->hw_core.description)); + mali_group_unlock(group); + return; + } + + rawstat = mali_mmu_get_rawstat(mmu); + MALI_DEBUG_CODE(status = mali_mmu_get_status(mmu)); + + MALI_DEBUG_PRINT(4, ("Mali MMU: Bottom half, interrupt 0x%08X, status 0x%08X\n", rawstat, status)); + + if (rawstat & (MALI_MMU_INTERRUPT_PAGE_FAULT | MALI_MMU_INTERRUPT_READ_BUS_ERROR)) { + /* An actual page fault has occurred. */ +#ifdef DEBUG + u32 fault_address = mali_mmu_get_page_fault_addr(mmu); + MALI_DEBUG_PRINT(2,("Mali MMU: Page fault detected at 0x%x from bus id %d of type %s on %s\n", + (void*)fault_address, + (status >> 6) & 0x1F, + (status & 32) ? "write" : "read", + mmu->hw_core.description)); +#endif + + mali_group_mmu_page_fault_and_unlock(group); + return; + } + + mali_group_unlock(group); +} + +_mali_osk_errcode_t mali_group_upper_half_gp(void *data) +{ + _mali_osk_errcode_t err = _MALI_OSK_ERR_FAULT; + struct mali_group *group = (struct mali_group *)data; + struct mali_gp_core *core = group->gp_core; + u32 irq_readout; + +#if defined(CONFIG_MALI_SHARED_INTERRUPTS) + if (MALI_FALSE == mali_pm_domain_lock_state(group->pm_domain)) { + goto out; + } +#endif + + irq_readout = mali_gp_get_int_stat(core); + + if (MALIGP2_REG_VAL_IRQ_MASK_NONE != irq_readout) { + /* Mask out all IRQs from this core until IRQ is handled */ + mali_gp_mask_all_interrupts(core); + + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_SINGLE|MALI_PROFILING_MAKE_EVENT_CHANNEL_GP(0)|MALI_PROFILING_EVENT_REASON_SINGLE_HW_INTERRUPT, irq_readout, 0, 0, 0, 0); + + /* We do need to handle this in a bottom half */ + _mali_osk_wq_schedule_work(group->bottom_half_work_gp); + + err = _MALI_OSK_ERR_OK; + goto out; + } + +out: +#if defined(CONFIG_MALI_SHARED_INTERRUPTS) + mali_pm_domain_unlock_state(group->pm_domain); +#endif + + return err; +} + +static void mali_group_bottom_half_gp(void *data) +{ + struct mali_group *group = (struct mali_group *)data; + u32 irq_readout; + u32 irq_errors; + + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_START|MALI_PROFILING_EVENT_CHANNEL_SOFTWARE|MALI_PROFILING_EVENT_REASON_START_STOP_SW_BOTTOM_HALF, 0, _mali_osk_get_tid(), MALI_PROFILING_MAKE_EVENT_DATA_CORE_GP(0), 0, 0); + + mali_group_lock(group); + + if ( MALI_FALSE == mali_group_power_is_on(group) ) { + MALI_PRINT_ERROR(("Mali group: Interrupt bottom half of %s when core is OFF.", mali_gp_get_hw_core_desc(group->gp_core))); + mali_group_unlock(group); + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP|MALI_PROFILING_EVENT_CHANNEL_SOFTWARE|MALI_PROFILING_EVENT_REASON_START_STOP_SW_BOTTOM_HALF, 0, _mali_osk_get_tid(), 0, 0, 0); + return; + } + + irq_readout = mali_gp_read_rawstat(group->gp_core); + + MALI_DEBUG_PRINT(4, ("Mali group: GP bottom half IRQ 0x%08X from core %s\n", irq_readout, mali_gp_get_hw_core_desc(group->gp_core))); + + if (irq_readout & (MALIGP2_REG_VAL_IRQ_VS_END_CMD_LST|MALIGP2_REG_VAL_IRQ_PLBU_END_CMD_LST)) { + u32 core_status = mali_gp_read_core_status(group->gp_core); + if (0 == (core_status & MALIGP2_REG_VAL_STATUS_MASK_ACTIVE)) { + MALI_DEBUG_PRINT(4, ("Mali group: GP job completed, calling group handler\n")); + group->core_timed_out = MALI_FALSE; + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP | + MALI_PROFILING_EVENT_CHANNEL_SOFTWARE | + MALI_PROFILING_EVENT_REASON_START_STOP_SW_BOTTOM_HALF, + 0, _mali_osk_get_tid(), 0, 0, 0); + + mali_group_complete_gp_and_unlock(group, MALI_TRUE); + return; + } + } + + /* + * Now lets look at the possible error cases (IRQ indicating error or timeout) + * END_CMD_LST, HANG and PLBU_OOM interrupts are not considered error. + */ + irq_errors = irq_readout & ~(MALIGP2_REG_VAL_IRQ_VS_END_CMD_LST|MALIGP2_REG_VAL_IRQ_PLBU_END_CMD_LST|MALIGP2_REG_VAL_IRQ_HANG|MALIGP2_REG_VAL_IRQ_PLBU_OUT_OF_MEM); + if (0 != irq_errors) { + MALI_PRINT_ERROR(("Mali group: Unknown interrupt 0x%08X from core %s, aborting job\n", irq_readout, mali_gp_get_hw_core_desc(group->gp_core))); + group->core_timed_out = MALI_FALSE; + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP | + MALI_PROFILING_EVENT_CHANNEL_SOFTWARE | + MALI_PROFILING_EVENT_REASON_START_STOP_SW_BOTTOM_HALF, + 0, _mali_osk_get_tid(), 0, 0, 0); + + mali_group_complete_gp_and_unlock(group, MALI_FALSE); + return; + } else if (group->core_timed_out) { /* SW timeout */ + group->core_timed_out = MALI_FALSE; + if (!_mali_osk_timer_pending(group->timeout_timer) && NULL != group->gp_running_job) { + MALI_PRINT(("Mali group: Job %d timed out\n", mali_gp_job_get_id(group->gp_running_job))); + + mali_group_complete_gp_and_unlock(group, MALI_FALSE); + return; + } + } else if (irq_readout & MALIGP2_REG_VAL_IRQ_PLBU_OUT_OF_MEM) { + /* GP wants more memory in order to continue. */ + MALI_DEBUG_PRINT(3, ("Mali group: PLBU needs more heap memory\n")); + + group->state = MALI_GROUP_STATE_OOM; + mali_group_unlock(group); /* Nothing to do on the HW side, so just release group lock right away */ + mali_gp_scheduler_oom(group, group->gp_running_job); + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP|MALI_PROFILING_EVENT_CHANNEL_SOFTWARE|MALI_PROFILING_EVENT_REASON_START_STOP_SW_BOTTOM_HALF, 0, _mali_osk_get_tid(), 0, 0, 0); + return; + } + + /* + * The only way to get here is if we only got one of two needed END_CMD_LST + * interrupts. Enable all but not the complete interrupt that has been + * received and continue to run. + */ + mali_gp_enable_interrupts(group->gp_core, irq_readout & (MALIGP2_REG_VAL_IRQ_PLBU_END_CMD_LST|MALIGP2_REG_VAL_IRQ_VS_END_CMD_LST)); + mali_group_unlock(group); + + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP|MALI_PROFILING_EVENT_CHANNEL_SOFTWARE|MALI_PROFILING_EVENT_REASON_START_STOP_SW_BOTTOM_HALF, 0, _mali_osk_get_tid(), 0, 0, 0); +} + +static void mali_group_post_process_job_gp(struct mali_group *group, mali_bool suspend) +{ + /* Stop the timeout timer. */ + _mali_osk_timer_del_async(group->timeout_timer); + + if (NULL == group->gp_running_job) { + /* Nothing to do */ + return; + } + + mali_gp_update_performance_counters(group->gp_core, group->gp_running_job, suspend); + +#if defined(CONFIG_MALI400_PROFILING) + if (suspend) { + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_SUSPEND|MALI_PROFILING_MAKE_EVENT_CHANNEL_GP(0), + mali_gp_job_get_perf_counter_value0(group->gp_running_job), + mali_gp_job_get_perf_counter_value1(group->gp_running_job), + mali_gp_job_get_perf_counter_src0(group->gp_running_job) | (mali_gp_job_get_perf_counter_src1(group->gp_running_job) << 8), + 0, 0); + } else { + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP|MALI_PROFILING_MAKE_EVENT_CHANNEL_GP(0), + mali_gp_job_get_perf_counter_value0(group->gp_running_job), + mali_gp_job_get_perf_counter_value1(group->gp_running_job), + mali_gp_job_get_perf_counter_src0(group->gp_running_job) | (mali_gp_job_get_perf_counter_src1(group->gp_running_job) << 8), + 0, 0); + + if ((MALI_HW_CORE_NO_COUNTER != mali_l2_cache_core_get_counter_src0(group->l2_cache_core[0])) && + (MALI_HW_CORE_NO_COUNTER != mali_l2_cache_core_get_counter_src1(group->l2_cache_core[0]))) + mali_group_report_l2_cache_counters_per_core(group, 0); + } +#endif + + mali_gp_job_set_current_heap_addr(group->gp_running_job, + mali_gp_read_plbu_alloc_start_addr(group->gp_core)); +} + +_mali_osk_errcode_t mali_group_upper_half_pp(void *data) +{ + _mali_osk_errcode_t err = _MALI_OSK_ERR_FAULT; + struct mali_group *group = (struct mali_group *)data; + struct mali_pp_core *core = group->pp_core; + u32 irq_readout; + +#if defined(CONFIG_MALI_SHARED_INTERRUPTS) + if (MALI_FALSE == mali_pm_domain_lock_state(group->pm_domain)) { + goto out; + } +#endif + + /* + * For Mali-450 there is one particular case we need to watch out for: + * + * Criteria 1) this function call can be due to a shared interrupt, + * and not necessary because this core signaled an interrupt. + * Criteria 2) this core is a part of a virtual group, and thus it should + * not do any post processing. + * Criteria 3) this core has actually indicated that is has completed by + * having set raw_stat/int_stat registers to != 0 + * + * If all this criteria is meet, then we could incorrectly start post + * processing on the wrong group object (this should only happen on the + * parent group) + */ +#if !defined(MALI_UPPER_HALF_SCHEDULING) + if (mali_group_is_in_virtual(group)) { + /* + * This check is done without the group lock held, which could lead to + * a potential race. This is however ok, since we will safely re-check + * this with the group lock held at a later stage. This is just an + * early out which will strongly benefit shared IRQ systems. + */ + err = _MALI_OSK_ERR_OK; + goto out; + } +#endif + + irq_readout = mali_pp_get_int_stat(core); + if (MALI200_REG_VAL_IRQ_MASK_NONE != irq_readout) { + /* Mask out all IRQs from this core until IRQ is handled */ + mali_pp_mask_all_interrupts(core); + +#if defined(CONFIG_MALI400_PROFILING) + /* Currently no support for this interrupt event for the virtual PP core */ + if (!mali_group_is_virtual(group)) { + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_SINGLE | + MALI_PROFILING_MAKE_EVENT_CHANNEL_PP(core->core_id) | + MALI_PROFILING_EVENT_REASON_SINGLE_HW_INTERRUPT, + irq_readout, 0, 0, 0, 0); + } +#endif + +#if defined(MALI_UPPER_HALF_SCHEDULING) + /* Check if job is complete without errors */ + if (MALI200_REG_VAL_IRQ_END_OF_FRAME == irq_readout) { + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_START | + MALI_PROFILING_EVENT_CHANNEL_SOFTWARE | + MALI_PROFILING_EVENT_REASON_START_STOP_SW_UPPER_HALF, + 0, 0, MALI_PROFILING_MAKE_EVENT_DATA_CORE_PP(core->core_id), 0, 0); + + MALI_DEBUG_PRINT(3, ("Mali PP: Job completed, calling group handler from upper half\n")); + + mali_group_lock(group); + + /* Check if job is complete without errors, again, after taking the group lock */ + irq_readout = mali_pp_read_rawstat(core); + if (MALI200_REG_VAL_IRQ_END_OF_FRAME != irq_readout) { + mali_pp_enable_interrupts(core); + mali_group_unlock(group); + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP | + MALI_PROFILING_EVENT_CHANNEL_SOFTWARE | + MALI_PROFILING_EVENT_REASON_START_STOP_SW_UPPER_HALF, + 0, 0, MALI_PROFILING_MAKE_EVENT_DATA_CORE_PP(core->core_id), 0, 0); + err = _MALI_OSK_ERR_OK; + goto out; + } + + if (mali_group_is_virtual(group)) { + u32 status_readout = mali_pp_read_status(group->pp_core); + if (status_readout & MALI200_REG_VAL_STATUS_RENDERING_ACTIVE) { + MALI_DEBUG_PRINT(6, ("Mali PP: Not all cores in broadcast completed\n")); + mali_pp_enable_interrupts(core); + mali_group_unlock(group); + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP | + MALI_PROFILING_EVENT_CHANNEL_SOFTWARE | + MALI_PROFILING_EVENT_REASON_START_STOP_SW_UPPER_HALF, + 0, 0, MALI_PROFILING_MAKE_EVENT_DATA_CORE_PP(core->core_id), 0, 0); + err = _MALI_OSK_ERR_OK; + goto out; + } + } + + if (mali_group_is_in_virtual(group)) { + /* We're member of a virtual group, so interrupt should be handled by the virtual group */ + mali_pp_enable_interrupts(core); + mali_group_unlock(group); + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP | + MALI_PROFILING_EVENT_CHANNEL_SOFTWARE | + MALI_PROFILING_EVENT_REASON_START_STOP_SW_UPPER_HALF, + 0, 0, MALI_PROFILING_MAKE_EVENT_DATA_CORE_PP(core->core_id), 0, 0); + err = _MALI_OSK_ERR_FAULT; + goto out; + } + + group->core_timed_out = MALI_FALSE; + + mali_group_complete_pp_and_unlock(group, MALI_TRUE, MALI_TRUE); + + /* No need to enable interrupts again, since the core will be reset while completing the job */ + + MALI_DEBUG_PRINT(6, ("Mali PP: Upper half job done\n")); + + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP | + MALI_PROFILING_EVENT_CHANNEL_SOFTWARE | + MALI_PROFILING_EVENT_REASON_START_STOP_SW_UPPER_HALF, + 0, 0, MALI_PROFILING_MAKE_EVENT_DATA_CORE_PP(core->core_id), 0, 0); + + err = _MALI_OSK_ERR_OK; + goto out; + } +#endif + + /* We do need to handle this in a bottom half */ + _mali_osk_wq_schedule_work(group->bottom_half_work_pp); + err = _MALI_OSK_ERR_OK; + goto out; + } + +out: +#if defined(CONFIG_MALI_SHARED_INTERRUPTS) + mali_pm_domain_unlock_state(group->pm_domain); +#endif + + return err; +} + +static void mali_group_bottom_half_pp(void *data) +{ + struct mali_group *group = (struct mali_group *)data; + struct mali_pp_core *core = group->pp_core; + u32 irq_readout; + u32 irq_errors; + + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_START | + MALI_PROFILING_EVENT_CHANNEL_SOFTWARE | + MALI_PROFILING_EVENT_REASON_START_STOP_SW_BOTTOM_HALF, + 0, _mali_osk_get_tid(), MALI_PROFILING_MAKE_EVENT_DATA_CORE_PP(core->core_id), 0, 0); + + mali_group_lock(group); + + if (mali_group_is_in_virtual(group)) { + /* We're member of a virtual group, so interrupt should be handled by the virtual group */ + mali_pp_enable_interrupts(core); + mali_group_unlock(group); + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP | + MALI_PROFILING_EVENT_CHANNEL_SOFTWARE | + MALI_PROFILING_EVENT_REASON_START_STOP_SW_BOTTOM_HALF, + 0, _mali_osk_get_tid(), 0, 0, 0); + return; + } + + if ( MALI_FALSE == mali_group_power_is_on(group) ) { + MALI_PRINT_ERROR(("Interrupt bottom half of %s when core is OFF.", mali_pp_get_hw_core_desc(core))); + mali_group_unlock(group); + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP | + MALI_PROFILING_EVENT_CHANNEL_SOFTWARE | + MALI_PROFILING_EVENT_REASON_START_STOP_SW_BOTTOM_HALF, + 0, _mali_osk_get_tid(), 0, 0, 0); + return; + } + + irq_readout = mali_pp_read_rawstat(group->pp_core); + + MALI_DEBUG_PRINT(4, ("Mali PP: Bottom half IRQ 0x%08X from core %s\n", irq_readout, mali_pp_get_hw_core_desc(group->pp_core))); + + /* Check if job is complete without errors */ + if (MALI200_REG_VAL_IRQ_END_OF_FRAME == irq_readout) { + if (mali_group_is_virtual(group)) { + u32 status_readout = mali_pp_read_status(group->pp_core); + + if (status_readout & MALI200_REG_VAL_STATUS_RENDERING_ACTIVE && !group->core_timed_out) { + MALI_DEBUG_PRINT(6, ("Mali PP: Not all cores in broadcast completed\n")); + mali_pp_enable_interrupts(core); + mali_group_unlock(group); + + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP | + MALI_PROFILING_EVENT_CHANNEL_SOFTWARE | + MALI_PROFILING_EVENT_REASON_START_STOP_SW_BOTTOM_HALF, + 0, _mali_osk_get_tid(), 0, 0, 0); + return; + } + } + + if (!group->core_timed_out) { + MALI_DEBUG_PRINT(3, ("Mali PP: Job completed, calling group handler\n")); + group->core_timed_out = MALI_FALSE; + + mali_group_complete_pp_and_unlock(group, MALI_TRUE, MALI_FALSE); + + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP | + MALI_PROFILING_EVENT_CHANNEL_SOFTWARE | + MALI_PROFILING_EVENT_REASON_START_STOP_SW_BOTTOM_HALF, + 0, _mali_osk_get_tid(), 0, 0, 0); + return; + } + } + + /* + * Now lets look at the possible error cases (IRQ indicating error or timeout) + * END_OF_FRAME and HANG interrupts are not considered error. + */ + irq_errors = irq_readout & ~(MALI200_REG_VAL_IRQ_END_OF_FRAME|MALI200_REG_VAL_IRQ_HANG); + if (0 != irq_errors) { + MALI_PRINT_ERROR(("Mali PP: Unexpected interrupt 0x%08X from core %s, aborting job\n", + irq_readout, mali_pp_get_hw_core_desc(group->pp_core))); + group->core_timed_out = MALI_FALSE; + + mali_group_complete_pp_and_unlock(group, MALI_FALSE, MALI_FALSE); + + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP | + MALI_PROFILING_EVENT_CHANNEL_SOFTWARE | + MALI_PROFILING_EVENT_REASON_START_STOP_SW_BOTTOM_HALF, + 0, _mali_osk_get_tid(), 0, 0, 0); + return; + } else if (group->core_timed_out) { /* SW timeout */ + group->core_timed_out = MALI_FALSE; + if (!_mali_osk_timer_pending(group->timeout_timer) && NULL != group->pp_running_job) { + MALI_PRINT(("Mali PP: Job %d timed out on core %s\n", + mali_pp_job_get_id(group->pp_running_job), mali_pp_get_hw_core_desc(core))); + + mali_group_complete_pp_and_unlock(group, MALI_FALSE, MALI_FALSE); + } else { + mali_group_unlock(group); + } + + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP | + MALI_PROFILING_EVENT_CHANNEL_SOFTWARE | + MALI_PROFILING_EVENT_REASON_START_STOP_SW_BOTTOM_HALF, + 0, _mali_osk_get_tid(), 0, 0, 0); + return; + } + + /* + * We should never get here, re-enable interrupts and continue + */ + if (0 == irq_readout) { + MALI_DEBUG_PRINT(3, ("Mali group: No interrupt found on core %s\n", + mali_pp_get_hw_core_desc(group->pp_core))); + } else { + MALI_PRINT_ERROR(("Mali group: Unhandled PP interrupt 0x%08X on %s\n", irq_readout, + mali_pp_get_hw_core_desc(group->pp_core))); + } + mali_pp_enable_interrupts(core); + mali_group_unlock(group); + + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP | + MALI_PROFILING_EVENT_CHANNEL_SOFTWARE | + MALI_PROFILING_EVENT_REASON_START_STOP_SW_BOTTOM_HALF, + 0, _mali_osk_get_tid(), 0, 0, 0); +} + +static void mali_group_post_process_job_pp(struct mali_group *group) +{ + MALI_ASSERT_GROUP_LOCKED(group); + + /* Stop the timeout timer. */ + _mali_osk_timer_del_async(group->timeout_timer); + + if (NULL != group->pp_running_job) { + if (MALI_TRUE == mali_group_is_virtual(group)) { + struct mali_group *child; + struct mali_group *temp; + + /* update performance counters from each physical pp core within this virtual group */ + _MALI_OSK_LIST_FOREACHENTRY(child, temp, &group->group_list, struct mali_group, group_list) { + mali_pp_update_performance_counters(group->pp_core, child->pp_core, group->pp_running_job, mali_pp_core_get_id(child->pp_core)); + } + +#if defined(CONFIG_MALI400_PROFILING) + /* send profiling data per physical core */ + _MALI_OSK_LIST_FOREACHENTRY(child, temp, &group->group_list, struct mali_group, group_list) { + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP| + MALI_PROFILING_MAKE_EVENT_CHANNEL_PP(mali_pp_core_get_id(child->pp_core))| + MALI_PROFILING_EVENT_REASON_START_STOP_HW_VIRTUAL, + mali_pp_job_get_perf_counter_value0(group->pp_running_job, mali_pp_core_get_id(child->pp_core)), + mali_pp_job_get_perf_counter_value1(group->pp_running_job, mali_pp_core_get_id(child->pp_core)), + mali_pp_job_get_perf_counter_src0(group->pp_running_job, group->pp_running_sub_job) | (mali_pp_job_get_perf_counter_src1(group->pp_running_job, group->pp_running_sub_job) << 8), + 0, 0); + } + if (0 != group->l2_cache_core_ref_count[0]) { + if ((MALI_HW_CORE_NO_COUNTER != mali_l2_cache_core_get_counter_src0(group->l2_cache_core[0])) && + (MALI_HW_CORE_NO_COUNTER != mali_l2_cache_core_get_counter_src1(group->l2_cache_core[0]))) { + mali_group_report_l2_cache_counters_per_core(group, mali_l2_cache_get_id(group->l2_cache_core[0])); + } + } + if (0 != group->l2_cache_core_ref_count[1]) { + if ((MALI_HW_CORE_NO_COUNTER != mali_l2_cache_core_get_counter_src0(group->l2_cache_core[1])) && + (MALI_HW_CORE_NO_COUNTER != mali_l2_cache_core_get_counter_src1(group->l2_cache_core[1]))) { + mali_group_report_l2_cache_counters_per_core(group, mali_l2_cache_get_id(group->l2_cache_core[1])); + } + } + +#endif + } else { + /* update performance counters for a physical group's pp core */ + mali_pp_update_performance_counters(group->pp_core, group->pp_core, group->pp_running_job, group->pp_running_sub_job); + +#if defined(CONFIG_MALI400_PROFILING) + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP| + MALI_PROFILING_MAKE_EVENT_CHANNEL_PP(mali_pp_core_get_id(group->pp_core))| + MALI_PROFILING_EVENT_REASON_START_STOP_HW_PHYSICAL, + mali_pp_job_get_perf_counter_value0(group->pp_running_job, group->pp_running_sub_job), + mali_pp_job_get_perf_counter_value1(group->pp_running_job, group->pp_running_sub_job), + mali_pp_job_get_perf_counter_src0(group->pp_running_job, group->pp_running_sub_job) | (mali_pp_job_get_perf_counter_src1(group->pp_running_job, group->pp_running_sub_job) << 8), + 0, 0); + + if ((MALI_HW_CORE_NO_COUNTER != mali_l2_cache_core_get_counter_src0(group->l2_cache_core[0])) && + (MALI_HW_CORE_NO_COUNTER != mali_l2_cache_core_get_counter_src1(group->l2_cache_core[0]))) { + mali_group_report_l2_cache_counters_per_core(group, mali_l2_cache_get_id(group->l2_cache_core[0])); + } +#endif + } + } +} + +static void mali_group_timeout(void *data) +{ + struct mali_group *group = (struct mali_group *)data; + + group->core_timed_out = MALI_TRUE; + + if (NULL != group->gp_core) { + MALI_DEBUG_PRINT(2, ("Mali group: TIMEOUT on %s\n", mali_gp_get_hw_core_desc(group->gp_core))); + _mali_osk_wq_schedule_work(group->bottom_half_work_gp); + } else { + MALI_DEBUG_PRINT(2, ("Mali group: TIMEOUT on %s\n", mali_pp_get_hw_core_desc(group->pp_core))); + _mali_osk_wq_schedule_work(group->bottom_half_work_pp); + } +} + +void mali_group_zap_session(struct mali_group *group, struct mali_session_data *session) +{ + MALI_DEBUG_ASSERT_POINTER(group); + MALI_DEBUG_ASSERT_POINTER(session); + + /* Early out - safe even if mutex is not held */ + if (group->session != session) return; + + mali_group_lock(group); + + mali_group_remove_session_if_unused(group, session); + + if (group->session == session) { + /* The Zap also does the stall and disable_stall */ + mali_bool zap_success = mali_mmu_zap_tlb(group->mmu); + if (MALI_TRUE != zap_success) { + MALI_DEBUG_PRINT(2, ("Mali memory unmap failed. Doing pagefault handling.\n")); + + mali_group_mmu_page_fault_and_unlock(group); + return; + } + } + + mali_group_unlock(group); +} + +#if defined(CONFIG_MALI400_PROFILING) +static void mali_group_report_l2_cache_counters_per_core(struct mali_group *group, u32 core_num) +{ + u32 source0 = 0; + u32 value0 = 0; + u32 source1 = 0; + u32 value1 = 0; + u32 profiling_channel = 0; + + switch(core_num) { + case 0: + profiling_channel = MALI_PROFILING_EVENT_TYPE_SINGLE | + MALI_PROFILING_EVENT_CHANNEL_GPU | + MALI_PROFILING_EVENT_REASON_SINGLE_GPU_L20_COUNTERS; + break; + case 1: + profiling_channel = MALI_PROFILING_EVENT_TYPE_SINGLE | + MALI_PROFILING_EVENT_CHANNEL_GPU | + MALI_PROFILING_EVENT_REASON_SINGLE_GPU_L21_COUNTERS; + break; + case 2: + profiling_channel = MALI_PROFILING_EVENT_TYPE_SINGLE | + MALI_PROFILING_EVENT_CHANNEL_GPU | + MALI_PROFILING_EVENT_REASON_SINGLE_GPU_L22_COUNTERS; + break; + default: + profiling_channel = MALI_PROFILING_EVENT_TYPE_SINGLE | + MALI_PROFILING_EVENT_CHANNEL_GPU | + MALI_PROFILING_EVENT_REASON_SINGLE_GPU_L20_COUNTERS; + break; + } + + if (0 == core_num) { + mali_l2_cache_core_get_counter_values(group->l2_cache_core[0], &source0, &value0, &source1, &value1); + } + if (1 == core_num) { + if (1 == mali_l2_cache_get_id(group->l2_cache_core[0])) { + mali_l2_cache_core_get_counter_values(group->l2_cache_core[0], &source0, &value0, &source1, &value1); + } else if (1 == mali_l2_cache_get_id(group->l2_cache_core[1])) { + mali_l2_cache_core_get_counter_values(group->l2_cache_core[1], &source0, &value0, &source1, &value1); + } + } + if (2 == core_num) { + if (2 == mali_l2_cache_get_id(group->l2_cache_core[0])) { + mali_l2_cache_core_get_counter_values(group->l2_cache_core[0], &source0, &value0, &source1, &value1); + } else if (2 == mali_l2_cache_get_id(group->l2_cache_core[1])) { + mali_l2_cache_core_get_counter_values(group->l2_cache_core[1], &source0, &value0, &source1, &value1); + } + } + + _mali_osk_profiling_add_event(profiling_channel, source1 << 8 | source0, value0, value1, 0, 0); +} +#endif /* #if defined(CONFIG_MALI400_PROFILING) */ + +mali_bool mali_group_is_enabled(struct mali_group *group) +{ + mali_bool enabled = MALI_TRUE; + + MALI_DEBUG_ASSERT_POINTER(group); + + mali_group_lock(group); + if (MALI_GROUP_STATE_DISABLED == group->state) { + enabled = MALI_FALSE; + } + mali_group_unlock(group); + + return enabled; +} + +void mali_group_enable(struct mali_group *group) +{ + MALI_DEBUG_ASSERT_POINTER(group); + MALI_DEBUG_ASSERT( NULL != mali_group_get_pp_core(group) + || NULL != mali_group_get_gp_core(group)); + + if (NULL != mali_group_get_pp_core(group)) { + mali_pp_scheduler_enable_group(group); + } else { + mali_gp_scheduler_enable_group(group); + } +} + +void mali_group_disable(struct mali_group *group) +{ + MALI_DEBUG_ASSERT_POINTER(group); + MALI_DEBUG_ASSERT( NULL != mali_group_get_pp_core(group) + || NULL != mali_group_get_gp_core(group)); + + if (NULL != mali_group_get_pp_core(group)) { + mali_pp_scheduler_disable_group(group); + } else { + mali_gp_scheduler_disable_group(group); + } +} + +static struct mali_pm_domain* mali_group_get_l2_domain(struct mali_group *group) +{ + MALI_DEBUG_ASSERT(NULL == group->l2_cache_core[1]); + + /* l2_cache_core[0] stores the related l2 domain */ + return group->l2_cache_core[0]->pm_domain; +} + +void mali_group_get_pm_domain_ref(struct mali_group *group) +{ + MALI_DEBUG_ASSERT_POINTER(group); + + /* Get group used l2 domain ref */ + mali_pm_domain_ref_get(mali_group_get_l2_domain(group)); + /* Get group used core domain ref */ + mali_pm_domain_ref_get(group->pm_domain); +} + +void mali_group_put_pm_domain_ref(struct mali_group *group) +{ + MALI_DEBUG_ASSERT_POINTER(group); + + /* Put group used core domain ref */ + mali_pm_domain_ref_put(group->pm_domain); + /* Put group used l2 domain ref */ + mali_pm_domain_ref_put(mali_group_get_l2_domain(group)); +} diff --git a/drivers/gpu/arm/mali/common/mali_group.h b/drivers/gpu/arm/mali/common/mali_group.h new file mode 100644 index 00000000000000..269403f2fd2aef --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_group.h @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2011-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_GROUP_H__ +#define __MALI_GROUP_H__ + +#include "linux/jiffies.h" +#include "mali_osk.h" +#include "mali_l2_cache.h" +#include "mali_mmu.h" +#include "mali_gp.h" +#include "mali_pp.h" +#include "mali_session.h" + +/** + * @brief Default max runtime [ms] for a core job - used by timeout timers + */ +#define MALI_MAX_JOB_RUNTIME_DEFAULT 4000 + +/** @brief A mali group object represents a MMU and a PP and/or a GP core. + * + */ +#define MALI_MAX_NUMBER_OF_GROUPS 10 + +enum mali_group_core_state { + MALI_GROUP_STATE_IDLE, + MALI_GROUP_STATE_WORKING, + MALI_GROUP_STATE_OOM, + MALI_GROUP_STATE_IN_VIRTUAL, + MALI_GROUP_STATE_JOINING_VIRTUAL, + MALI_GROUP_STATE_LEAVING_VIRTUAL, + MALI_GROUP_STATE_DISABLED, +}; + +/* Forward declaration from mali_pm_domain.h */ +struct mali_pm_domain; + +/** + * The structure represents a render group + * A render group is defined by all the cores that share the same Mali MMU + */ + +struct mali_group { + struct mali_mmu_core *mmu; + struct mali_session_data *session; + + mali_bool power_is_on; + enum mali_group_core_state state; + + struct mali_gp_core *gp_core; + struct mali_gp_job *gp_running_job; + + struct mali_pp_core *pp_core; + struct mali_pp_job *pp_running_job; + u32 pp_running_sub_job; + + struct mali_l2_cache_core *l2_cache_core[2]; + u32 l2_cache_core_ref_count[2]; + + struct mali_dlbu_core *dlbu_core; + struct mali_bcast_unit *bcast_core; + +#ifdef MALI_UPPER_HALF_SCHEDULING + _mali_osk_spinlock_irq_t *lock; +#else + _mali_osk_spinlock_t *lock; +#endif + + _mali_osk_list_t pp_scheduler_list; + + /* List used for virtual groups. For a virtual group, the list represents the + * head element. */ + _mali_osk_list_t group_list; + + struct mali_group *pm_domain_list; + struct mali_pm_domain *pm_domain; + + /* Parent virtual group (if any) */ + struct mali_group *parent_group; + + _mali_osk_wq_work_t *bottom_half_work_mmu; + _mali_osk_wq_work_t *bottom_half_work_gp; + _mali_osk_wq_work_t *bottom_half_work_pp; + + _mali_osk_timer_t *timeout_timer; + mali_bool core_timed_out; +}; + +/** @brief Create a new Mali group object + * + * @param cluster Pointer to the cluster to which the group is connected. + * @param mmu Pointer to the MMU that defines this group + * @return A pointer to a new group object + */ +struct mali_group *mali_group_create(struct mali_l2_cache_core *core, + struct mali_dlbu_core *dlbu, + struct mali_bcast_unit *bcast); + +_mali_osk_errcode_t mali_group_add_mmu_core(struct mali_group *group, struct mali_mmu_core* mmu_core); +void mali_group_remove_mmu_core(struct mali_group *group); + +_mali_osk_errcode_t mali_group_add_gp_core(struct mali_group *group, struct mali_gp_core* gp_core); +void mali_group_remove_gp_core(struct mali_group *group); + +_mali_osk_errcode_t mali_group_add_pp_core(struct mali_group *group, struct mali_pp_core* pp_core); +void mali_group_remove_pp_core(struct mali_group *group); + +void mali_group_set_pm_domain(struct mali_group *group, struct mali_pm_domain *domain); + +void mali_group_delete(struct mali_group *group); + +/** @brief Virtual groups */ +void mali_group_add_group(struct mali_group *parent, struct mali_group *child, mali_bool update_hw); +void mali_group_remove_group(struct mali_group *parent, struct mali_group *child); +struct mali_group *mali_group_acquire_group(struct mali_group *parent); + +MALI_STATIC_INLINE mali_bool mali_group_is_virtual(struct mali_group *group) +{ +#if defined(CONFIG_MALI450) + return (NULL != group->dlbu_core); +#else + return MALI_FALSE; +#endif +} + +/** @brief Check if a group is considered as part of a virtual group + * + * @note A group is considered to be "part of" a virtual group also during the transition + * in to / out of the virtual group. + */ +MALI_STATIC_INLINE mali_bool mali_group_is_in_virtual(struct mali_group *group) +{ +#if defined(CONFIG_MALI450) + return (MALI_GROUP_STATE_IN_VIRTUAL == group->state || + MALI_GROUP_STATE_JOINING_VIRTUAL == group->state || + MALI_GROUP_STATE_LEAVING_VIRTUAL == group->state); +#else + return MALI_FALSE; +#endif +} + +/** @brief Reset group + * + * This function will reset the entire group, including all the cores present in the group. + * + * @param group Pointer to the group to reset + */ +void mali_group_reset(struct mali_group *group); + +/** @brief Zap MMU TLB on all groups + * + * Zap TLB on group if \a session is active. + */ +void mali_group_zap_session(struct mali_group* group, struct mali_session_data *session); + +/** @brief Get pointer to GP core object + */ +struct mali_gp_core* mali_group_get_gp_core(struct mali_group *group); + +/** @brief Get pointer to PP core object + */ +struct mali_pp_core* mali_group_get_pp_core(struct mali_group *group); + +/** @brief Lock group object + * + * Most group functions will lock the group object themselves. The expection is + * the group_bottom_half which requires the group to be locked on entry. + * + * @param group Pointer to group to lock + */ +void mali_group_lock(struct mali_group *group); + +/** @brief Unlock group object + * + * @param group Pointer to group to unlock + */ +void mali_group_unlock(struct mali_group *group); +#ifdef DEBUG +void mali_group_assert_locked(struct mali_group *group); +#define MALI_ASSERT_GROUP_LOCKED(group) mali_group_assert_locked(group) +#else +#define MALI_ASSERT_GROUP_LOCKED(group) +#endif + +/** @brief Start GP job + */ +void mali_group_start_gp_job(struct mali_group *group, struct mali_gp_job *job); +/** @brief Start fragment of PP job + */ +void mali_group_start_pp_job(struct mali_group *group, struct mali_pp_job *job, u32 sub_job); + +/** @brief Resume GP job that suspended waiting for more heap memory + */ +struct mali_gp_job *mali_group_resume_gp_with_new_heap(struct mali_group *group, u32 job_id, u32 start_addr, u32 end_addr); +/** @brief Abort GP job + * + * Used to abort suspended OOM jobs when user space failed to allocte more memory. + */ +void mali_group_abort_gp_job(struct mali_group *group, u32 job_id); +/** @brief Abort all GP jobs from \a session + * + * Used on session close when terminating all running and queued jobs from \a session. + */ +void mali_group_abort_session(struct mali_group *group, struct mali_session_data *session); + +mali_bool mali_group_power_is_on(struct mali_group *group); +void mali_group_power_on_group(struct mali_group *group); +void mali_group_power_off_group(struct mali_group *group, mali_bool power_status); +void mali_group_power_on(void); + +/** @brief Prepare group for power off + * + * Update the group's state and prepare for the group to be powered off. + * + * If do_power_change is MALI_FALSE group session will be set to NULL so that + * no more activity will happen to this group, but the power state flag will be + * left unchanged. + * + * @do_power_change MALI_TRUE if power status is to be updated + */ +void mali_group_power_off(mali_bool do_power_change); + +struct mali_group *mali_group_get_glob_group(u32 index); +u32 mali_group_get_glob_num_groups(void); + +u32 mali_group_dump_state(struct mali_group *group, char *buf, u32 size); + +/* MMU-related functions */ +_mali_osk_errcode_t mali_group_upper_half_mmu(void * data); + +/* GP-related functions */ +_mali_osk_errcode_t mali_group_upper_half_gp(void *data); + +/* PP-related functions */ +_mali_osk_errcode_t mali_group_upper_half_pp(void *data); + +/** @brief Check if group is enabled + * + * @param group group to check + * @return MALI_TRUE if enabled, MALI_FALSE if not + */ +mali_bool mali_group_is_enabled(struct mali_group *group); + +/** @brief Enable group + * + * An enabled job is put on the idle scheduler list and can be used to handle jobs. Does nothing if + * group is already enabled. + * + * @param group group to enable + */ +void mali_group_enable(struct mali_group *group); + +/** @brief Disable group + * + * A disabled group will no longer be used by the scheduler. If part of a virtual group, the group + * will be removed before being disabled. Cores part of a disabled group is safe to power down. + * + * @param group group to disable + */ +void mali_group_disable(struct mali_group *group); + +MALI_STATIC_INLINE mali_bool mali_group_virtual_disable_if_empty(struct mali_group *group) +{ + mali_bool empty = MALI_FALSE; + + MALI_ASSERT_GROUP_LOCKED(group); + MALI_DEBUG_ASSERT(mali_group_is_virtual(group)); + + if (_mali_osk_list_empty(&group->group_list)) { + group->state = MALI_GROUP_STATE_DISABLED; + group->session = NULL; + + empty = MALI_TRUE; + } + + return empty; +} + +MALI_STATIC_INLINE mali_bool mali_group_virtual_enable_if_empty(struct mali_group *group) +{ + mali_bool empty = MALI_FALSE; + + MALI_ASSERT_GROUP_LOCKED(group); + MALI_DEBUG_ASSERT(mali_group_is_virtual(group)); + + if (_mali_osk_list_empty(&group->group_list)) { + MALI_DEBUG_ASSERT(MALI_GROUP_STATE_DISABLED == group->state); + + group->state = MALI_GROUP_STATE_IDLE; + + empty = MALI_TRUE; + } + + return empty; +} + +/* Get group used l2 domain and core domain ref */ +void mali_group_get_pm_domain_ref(struct mali_group *group); +/* Put group used l2 domain and core domain ref */ +void mali_group_put_pm_domain_ref(struct mali_group *group); + +#endif /* __MALI_GROUP_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_hw_core.c b/drivers/gpu/arm/mali/common/mali_hw_core.c new file mode 100644 index 00000000000000..f8734206caad54 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_hw_core.c @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2011-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_hw_core.h" +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_osk_mali.h" + +_mali_osk_errcode_t mali_hw_core_create(struct mali_hw_core *core, const _mali_osk_resource_t *resource, u32 reg_size) +{ + core->phys_addr = resource->base; + core->phys_offset = resource->base - _mali_osk_resource_base_address(); + core->description = resource->description; + core->size = reg_size; + + MALI_DEBUG_ASSERT(core->phys_offset < core->phys_addr); + + if (_MALI_OSK_ERR_OK == _mali_osk_mem_reqregion(core->phys_addr, core->size, core->description)) { + core->mapped_registers = _mali_osk_mem_mapioregion(core->phys_addr, core->size, core->description); + if (NULL != core->mapped_registers) { + return _MALI_OSK_ERR_OK; + } else { + MALI_PRINT_ERROR(("Failed to map memory region for core %s at phys_addr 0x%08X\n", core->description, core->phys_addr)); + } + _mali_osk_mem_unreqregion(core->phys_addr, core->size); + } else { + MALI_PRINT_ERROR(("Failed to request memory region for core %s at phys_addr 0x%08X\n", core->description, core->phys_addr)); + } + + return _MALI_OSK_ERR_FAULT; +} + +void mali_hw_core_delete(struct mali_hw_core *core) +{ + _mali_osk_mem_unmapioregion(core->phys_addr, core->size, core->mapped_registers); + core->mapped_registers = NULL; + _mali_osk_mem_unreqregion(core->phys_addr, core->size); +} diff --git a/drivers/gpu/arm/mali/common/mali_hw_core.h b/drivers/gpu/arm/mali/common/mali_hw_core.h new file mode 100644 index 00000000000000..d2c44a9d537969 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_hw_core.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2011-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_HW_CORE_H__ +#define __MALI_HW_CORE_H__ + +#include "mali_osk.h" +#include "mali_kernel_common.h" + +/** + * The common parts for all Mali HW cores (GP, PP, MMU, L2 and PMU) + * This struct is embedded inside all core specific structs. + */ +struct mali_hw_core { + u32 phys_addr; /**< Physical address of the registers */ + u32 phys_offset; /**< Offset from start of Mali to registers */ + u32 size; /**< Size of registers */ + mali_io_address mapped_registers; /**< Virtual mapping of the registers */ + const char* description; /**< Name of unit (as specified in device configuration) */ +}; + +#define MALI_REG_POLL_COUNT_FAST 1000 +#define MALI_REG_POLL_COUNT_SLOW 1000000 + +_mali_osk_errcode_t mali_hw_core_create(struct mali_hw_core *core, const _mali_osk_resource_t *resource, u32 reg_size); +void mali_hw_core_delete(struct mali_hw_core *core); + +MALI_STATIC_INLINE u32 mali_hw_core_register_read(struct mali_hw_core *core, u32 relative_address) +{ + u32 read_val; + read_val = _mali_osk_mem_ioread32(core->mapped_registers, relative_address); + MALI_DEBUG_PRINT(6, ("register_read for core %s, relative addr=0x%04X, val=0x%08X\n", + core->description, relative_address, read_val)); + return read_val; +} + +MALI_STATIC_INLINE void mali_hw_core_register_write_relaxed(struct mali_hw_core *core, u32 relative_address, u32 new_val) +{ + MALI_DEBUG_PRINT(6, ("register_write_relaxed for core %s, relative addr=0x%04X, val=0x%08X\n", + core->description, relative_address, new_val)); + _mali_osk_mem_iowrite32_relaxed(core->mapped_registers, relative_address, new_val); +} + +/* Conditionally write a register. + * The register will only be written if the new value is different from the old_value. + * If the new value is different, the old value will also be updated */ +MALI_STATIC_INLINE void mali_hw_core_register_write_relaxed_conditional(struct mali_hw_core *core, u32 relative_address, u32 new_val, const u32 old_val) +{ + MALI_DEBUG_PRINT(6, ("register_write_relaxed for core %s, relative addr=0x%04X, val=0x%08X\n", + core->description, relative_address, new_val)); + if(old_val != new_val) { + _mali_osk_mem_iowrite32_relaxed(core->mapped_registers, relative_address, new_val); + } +} + + +MALI_STATIC_INLINE void mali_hw_core_register_write(struct mali_hw_core *core, u32 relative_address, u32 new_val) +{ + MALI_DEBUG_PRINT(6, ("register_write for core %s, relative addr=0x%04X, val=0x%08X\n", + core->description, relative_address, new_val)); + _mali_osk_mem_iowrite32(core->mapped_registers, relative_address, new_val); +} + +MALI_STATIC_INLINE void mali_hw_core_register_write_array_relaxed(struct mali_hw_core *core, u32 relative_address, u32 *write_array, u32 nr_of_regs) +{ + u32 i; + MALI_DEBUG_PRINT(6, ("register_write_array: for core %s, relative addr=0x%04X, nr of regs=%u\n", + core->description,relative_address, nr_of_regs)); + + /* Do not use burst writes against the registers */ + for (i = 0; i< nr_of_regs; i++) { + mali_hw_core_register_write_relaxed(core, relative_address + i*4, write_array[i]); + } +} + +/* Conditionally write a set of registers. + * The register will only be written if the new value is different from the old_value. + * If the new value is different, the old value will also be updated */ +MALI_STATIC_INLINE void mali_hw_core_register_write_array_relaxed_conditional(struct mali_hw_core *core, u32 relative_address, u32 *write_array, u32 nr_of_regs, const u32* old_array) +{ + u32 i; + MALI_DEBUG_PRINT(6, ("register_write_array: for core %s, relative addr=0x%04X, nr of regs=%u\n", + core->description,relative_address, nr_of_regs)); + + /* Do not use burst writes against the registers */ + for (i = 0; i< nr_of_regs; i++) { + if(old_array[i] != write_array[i]) { + mali_hw_core_register_write_relaxed(core, relative_address + i*4, write_array[i]); + } + } +} + +#endif /* __MALI_HW_CORE_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_kernel_common.h b/drivers/gpu/arm/mali/common/mali_kernel_common.h new file mode 100644 index 00000000000000..fe57539fcda11c --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_common.h @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2010, 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_COMMON_H__ +#define __MALI_KERNEL_COMMON_H__ + +#include "mali_osk.h" + +/* Make sure debug is defined when it should be */ +#ifndef DEBUG +#if defined(_DEBUG) +#define DEBUG +#endif +#endif + +/* The file include several useful macros for error checking, debugging and printing. + * - MALI_PRINTF(...) Do not use this function: Will be included in Release builds. + * - MALI_DEBUG_PRINT(nr, (X) ) Prints the second argument if nr<=MALI_DEBUG_LEVEL. + * - MALI_DEBUG_ERROR( (X) ) Prints an errortext, a source trace, and the given error message. + * - MALI_DEBUG_ASSERT(exp,(X)) If the asserted expr is false, the program will exit. + * - MALI_DEBUG_ASSERT_POINTER(pointer) Triggers if the pointer is a zero pointer. + * - MALI_DEBUG_CODE( X ) The code inside the macro is only compiled in Debug builds. + * + * The (X) means that you must add an extra parenthesis around the argumentlist. + * + * The printf function: MALI_PRINTF(...) is routed to _mali_osk_debugmsg + * + * Suggested range for the DEBUG-LEVEL is [1:6] where + * [1:2] Is messages with highest priority, indicate possible errors. + * [3:4] Is messages with medium priority, output important variables. + * [5:6] Is messages with low priority, used during extensive debugging. + */ + +/** +* Fundamental error macro. Reports an error code. This is abstracted to allow us to +* easily switch to a different error reporting method if we want, and also to allow +* us to search for error returns easily. +* +* Note no closing semicolon - this is supplied in typical usage: +* +* MALI_ERROR(MALI_ERROR_OUT_OF_MEMORY); +*/ +#define MALI_ERROR(error_code) return (error_code) + +/** + * Basic error macro, to indicate success. + * Note no closing semicolon - this is supplied in typical usage: + * + * MALI_SUCCESS; + */ +#define MALI_SUCCESS MALI_ERROR(_MALI_OSK_ERR_OK) + +/** + * Basic error macro. This checks whether the given condition is true, and if not returns + * from this function with the supplied error code. This is a macro so that we can override it + * for stress testing. + * + * Note that this uses the do-while-0 wrapping to ensure that we don't get problems with dangling + * else clauses. Note also no closing semicolon - this is supplied in typical usage: + * + * MALI_CHECK((p!=NULL), ERROR_NO_OBJECT); + */ +#define MALI_CHECK(condition, error_code) do { if(!(condition)) MALI_ERROR(error_code); } while(0) + +/** + * Error propagation macro. If the expression given is anything other than _MALI_OSK_NO_ERROR, + * then the value is returned from the enclosing function as an error code. This effectively + * acts as a guard clause, and propagates error values up the call stack. This uses a + * temporary value to ensure that the error expression is not evaluated twice. + * If the counter for forcing a failure has been set using _mali_force_error, this error will be + * returned without evaluating the expression in MALI_CHECK_NO_ERROR + */ +#define MALI_CHECK_NO_ERROR(expression) \ + do { _mali_osk_errcode_t _check_no_error_result=(expression); \ + if(_check_no_error_result != _MALI_OSK_ERR_OK) \ + MALI_ERROR(_check_no_error_result); \ + } while(0) + +/** + * Pointer check macro. Checks non-null pointer. + */ +#define MALI_CHECK_NON_NULL(pointer, error_code) MALI_CHECK( ((pointer)!=NULL), (error_code) ) + +/** + * Error macro with goto. This checks whether the given condition is true, and if not jumps + * to the specified label using a goto. The label must therefore be local to the function in + * which this macro appears. This is most usually used to execute some clean-up code before + * exiting with a call to ERROR. + * + * Like the other macros, this is a macro to allow us to override the condition if we wish, + * e.g. to force an error during stress testing. + */ +#define MALI_CHECK_GOTO(condition, label) do { if(!(condition)) goto label; } while(0) + +/** + * Explicitly ignore a parameter passed into a function, to suppress compiler warnings. + * Should only be used with parameter names. + */ +#define MALI_IGNORE(x) x=x + +#define MALI_PRINTF(args) _mali_osk_dbgmsg args; + +#define MALI_PRINT_ERROR(args) do{ \ + MALI_PRINTF(("Mali: ERR: %s\n" ,__FILE__)); \ + MALI_PRINTF((" %s()%4d\n ", __FUNCTION__, __LINE__)) ; \ + MALI_PRINTF(args); \ + MALI_PRINTF(("\n")); \ + } while(0) + +#define MALI_PRINT(args) do{ \ + MALI_PRINTF(("Mali: ")); \ + MALI_PRINTF(args); \ + } while (0) + +#ifdef DEBUG +#ifndef mali_debug_level +extern int mali_debug_level; +#endif + +#define MALI_DEBUG_CODE(code) code +#define MALI_DEBUG_PRINT(level, args) do { \ + if((level) <= mali_debug_level)\ + {MALI_PRINTF(("Mali<" #level ">: ")); MALI_PRINTF(args); } \ + } while (0) + +#define MALI_DEBUG_PRINT_ERROR(args) MALI_PRINT_ERROR(args) + +#define MALI_DEBUG_PRINT_IF(level,condition,args) \ + if((condition)&&((level) <= mali_debug_level))\ + {MALI_PRINTF(("Mali<" #level ">: ")); MALI_PRINTF(args); } + +#define MALI_DEBUG_PRINT_ELSE(level, args)\ + else if((level) <= mali_debug_level)\ + { MALI_PRINTF(("Mali<" #level ">: ")); MALI_PRINTF(args); } + +/** + * @note these variants of DEBUG ASSERTS will cause a debugger breakpoint + * to be entered (see _mali_osk_break() ). An alternative would be to call + * _mali_osk_abort(), on OSs that support it. + */ +#define MALI_DEBUG_PRINT_ASSERT(condition, args) do {if( !(condition)) { MALI_PRINT_ERROR(args); _mali_osk_break(); } } while(0) +#define MALI_DEBUG_ASSERT_POINTER(pointer) do {if( (pointer)== NULL) {MALI_PRINT_ERROR(("NULL pointer " #pointer)); _mali_osk_break();} } while(0) +#define MALI_DEBUG_ASSERT(condition) do {if( !(condition)) {MALI_PRINT_ERROR(("ASSERT failed: " #condition )); _mali_osk_break();} } while(0) + +#else /* DEBUG */ + +#define MALI_DEBUG_CODE(code) +#define MALI_DEBUG_PRINT(string,args) do {} while(0) +#define MALI_DEBUG_PRINT_ERROR(args) do {} while(0) +#define MALI_DEBUG_PRINT_IF(level,condition,args) do {} while(0) +#define MALI_DEBUG_PRINT_ELSE(level,condition,args) do {} while(0) +#define MALI_DEBUG_PRINT_ASSERT(condition,args) do {} while(0) +#define MALI_DEBUG_ASSERT_POINTER(pointer) do {} while(0) +#define MALI_DEBUG_ASSERT(condition) do {} while(0) + +#endif /* DEBUG */ + +/** + * variables from user space cannot be dereferenced from kernel space; tagging them + * with __user allows the GCC compiler to generate a warning. Other compilers may + * not support this so we define it here as an empty macro if the compiler doesn't + * define it. + */ +#ifndef __user +#define __user +#endif + +#endif /* __MALI_KERNEL_COMMON_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_kernel_core.c b/drivers/gpu/arm/mali/common/mali_kernel_core.c new file mode 100644 index 00000000000000..e0994246399e5b --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_core.c @@ -0,0 +1,1399 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_session.h" +#include "mali_osk.h" +#include "mali_osk_mali.h" +#include "mali_ukk.h" +#include "mali_kernel_core.h" +#include "mali_memory.h" +#include "mali_mem_validation.h" +#include "mali_mmu.h" +#include "mali_mmu_page_directory.h" +#include "mali_dlbu.h" +#include "mali_broadcast.h" +#include "mali_gp.h" +#include "mali_pp.h" +#include "mali_gp_scheduler.h" +#include "mali_pp_scheduler.h" +#include "mali_pp_job.h" +#include "mali_group.h" +#include "mali_pm.h" +#include "mali_pmu.h" +#include "mali_scheduler.h" +#include "mali_kernel_utilization.h" +#include "mali_l2_cache.h" +#include "mali_dma.h" +#include "mali_timeline.h" +#include "mali_soft_job.h" +#include "mali_pm_domain.h" +#if defined(CONFIG_MALI400_PROFILING) +#include "mali_osk_profiling.h" +#endif +#if defined(CONFIG_MALI400_INTERNAL_PROFILING) +#include "mali_profiling_internal.h" +#endif + + +/* Mali GPU memory. Real values come from module parameter or from device specific data */ +unsigned int mali_dedicated_mem_start = 0; +unsigned int mali_dedicated_mem_size = 0; +unsigned int mali_shared_mem_size = 0; + +/* Frame buffer memory to be accessible by Mali GPU */ +int mali_fb_start = 0; +int mali_fb_size = 0; + +/* Mali max job runtime */ +extern int mali_max_job_runtime; + +/** Start profiling from module load? */ +int mali_boot_profiling = 0; + +/** Limits for the number of PP cores behind each L2 cache. */ +int mali_max_pp_cores_group_1 = 0xFF; +int mali_max_pp_cores_group_2 = 0xFF; + +int mali_inited_pp_cores_group_1 = 0; +int mali_inited_pp_cores_group_2 = 0; + +static _mali_product_id_t global_product_id = _MALI_PRODUCT_ID_UNKNOWN; +static u32 global_gpu_base_address = 0; +static u32 global_gpu_major_version = 0; +static u32 global_gpu_minor_version = 0; + +mali_bool mali_gpu_class_is_mali450 = MALI_FALSE; + +static _mali_osk_errcode_t mali_set_global_gpu_base_address(void) +{ + global_gpu_base_address = _mali_osk_resource_base_address(); + if (0 == global_gpu_base_address) { + return _MALI_OSK_ERR_ITEM_NOT_FOUND; + } + + return _MALI_OSK_ERR_OK; +} + +static u32 mali_get_bcast_id(_mali_osk_resource_t *resource_pp) +{ + switch (resource_pp->base - global_gpu_base_address) { + case 0x08000: + case 0x20000: /* fall-through for aliased mapping */ + return 0x01; + case 0x0A000: + case 0x22000: /* fall-through for aliased mapping */ + return 0x02; + case 0x0C000: + case 0x24000: /* fall-through for aliased mapping */ + return 0x04; + case 0x0E000: + case 0x26000: /* fall-through for aliased mapping */ + return 0x08; + case 0x28000: + return 0x10; + case 0x2A000: + return 0x20; + case 0x2C000: + return 0x40; + case 0x2E000: + return 0x80; + default: + return 0; + } +} + +static _mali_osk_errcode_t mali_parse_product_info(void) +{ + /* + * Mali-200 has the PP core first, while Mali-300, Mali-400 and Mali-450 have the GP core first. + * Look at the version register for the first PP core in order to determine the GPU HW revision. + */ + + u32 first_pp_offset; + _mali_osk_resource_t first_pp_resource; + + /* Find out where the first PP core is located */ + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x8000, NULL)) { + /* Mali-300/400/450 */ + first_pp_offset = 0x8000; + } else { + /* Mali-200 */ + first_pp_offset = 0x0000; + } + + /* Find the first PP core resource (again) */ + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + first_pp_offset, &first_pp_resource)) { + /* Create a dummy PP object for this core so that we can read the version register */ + struct mali_group *group = mali_group_create(NULL, NULL, NULL); + if (NULL != group) { + struct mali_pp_core *pp_core = mali_pp_create(&first_pp_resource, group, MALI_FALSE, mali_get_bcast_id(&first_pp_resource)); + if (NULL != pp_core) { + u32 pp_version = mali_pp_core_get_version(pp_core); + mali_group_delete(group); + + global_gpu_major_version = (pp_version >> 8) & 0xFF; + global_gpu_minor_version = pp_version & 0xFF; + + switch (pp_version >> 16) { + case MALI200_PP_PRODUCT_ID: + global_product_id = _MALI_PRODUCT_ID_MALI200; + MALI_DEBUG_PRINT(2, ("Found Mali GPU Mali-200 r%up%u\n", global_gpu_major_version, global_gpu_minor_version)); + MALI_PRINT_ERROR(("Mali-200 is not supported by this driver.\n")); + _mali_osk_abort(); + break; + case MALI300_PP_PRODUCT_ID: + global_product_id = _MALI_PRODUCT_ID_MALI300; + MALI_DEBUG_PRINT(2, ("Found Mali GPU Mali-300 r%up%u\n", global_gpu_major_version, global_gpu_minor_version)); + break; + case MALI400_PP_PRODUCT_ID: + global_product_id = _MALI_PRODUCT_ID_MALI400; + MALI_DEBUG_PRINT(2, ("Found Mali GPU Mali-400 MP r%up%u\n", global_gpu_major_version, global_gpu_minor_version)); + break; + case MALI450_PP_PRODUCT_ID: + global_product_id = _MALI_PRODUCT_ID_MALI450; + MALI_DEBUG_PRINT(2, ("Found Mali GPU Mali-450 MP r%up%u\n", global_gpu_major_version, global_gpu_minor_version)); + break; + default: + MALI_DEBUG_PRINT(2, ("Found unknown Mali GPU (r%up%u)\n", global_gpu_major_version, global_gpu_minor_version)); + return _MALI_OSK_ERR_FAULT; + } + + return _MALI_OSK_ERR_OK; + } else { + MALI_PRINT_ERROR(("Failed to create initial PP object\n")); + } + } else { + MALI_PRINT_ERROR(("Failed to create initial group object\n")); + } + } else { + MALI_PRINT_ERROR(("First PP core not specified in config file\n")); + } + + return _MALI_OSK_ERR_FAULT; +} + + +static void mali_resource_count(u32 *pp_count, u32 *l2_count) +{ + *pp_count = 0; + *l2_count = 0; + + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x08000, NULL)) { + ++(*pp_count); + } + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x0A000, NULL)) { + ++(*pp_count); + } + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x0C000, NULL)) { + ++(*pp_count); + } + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x0E000, NULL)) { + ++(*pp_count); + } + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x28000, NULL)) { + ++(*pp_count); + } + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x2A000, NULL)) { + ++(*pp_count); + } + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x2C000, NULL)) { + ++(*pp_count); + } + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x2E000, NULL)) { + ++(*pp_count); + } + + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x1000, NULL)) { + ++(*l2_count); + } + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x10000, NULL)) { + ++(*l2_count); + } + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x11000, NULL)) { + ++(*l2_count); + } +} + +static void mali_delete_groups(void) +{ + struct mali_group *group; + + group = mali_group_get_glob_group(0); + while (NULL != group) { + mali_group_delete(group); + group = mali_group_get_glob_group(0); + } + + MALI_DEBUG_ASSERT(0 == mali_group_get_glob_num_groups()); +} + +static void mali_delete_l2_cache_cores(void) +{ + struct mali_l2_cache_core *l2; + + l2 = mali_l2_cache_core_get_glob_l2_core(0); + while (NULL != l2) { + mali_l2_cache_delete(l2); + l2 = mali_l2_cache_core_get_glob_l2_core(0); + } + + MALI_DEBUG_ASSERT(0 == mali_l2_cache_core_get_glob_num_l2_cores()); +} + +static struct mali_l2_cache_core *mali_create_l2_cache_core(_mali_osk_resource_t *resource) +{ + struct mali_l2_cache_core *l2_cache = NULL; + + if (NULL != resource) { + + MALI_DEBUG_PRINT(3, ("Found L2 cache %s\n", resource->description)); + + l2_cache = mali_l2_cache_create(resource); + if (NULL == l2_cache) { + MALI_PRINT_ERROR(("Failed to create L2 cache object\n")); + return NULL; + } + } + MALI_DEBUG_PRINT(3, ("Created L2 cache core object\n")); + + return l2_cache; +} + +static _mali_osk_errcode_t mali_parse_config_l2_cache(void) +{ + struct mali_l2_cache_core *l2_cache = NULL; + + if (mali_is_mali400()) { + _mali_osk_resource_t l2_resource; + if (_MALI_OSK_ERR_OK != _mali_osk_resource_find(global_gpu_base_address + 0x1000, &l2_resource)) { + MALI_DEBUG_PRINT(3, ("Did not find required Mali L2 cache in config file\n")); + return _MALI_OSK_ERR_FAULT; + } + + l2_cache = mali_create_l2_cache_core(&l2_resource); + if (NULL == l2_cache) { + return _MALI_OSK_ERR_FAULT; + } + mali_pm_domain_add_l2(mali_pmu_get_domain_mask(MALI_L20_DOMAIN_INDEX), l2_cache); + } else if (mali_is_mali450()) { + /* + * L2 for GP at 0x10000 + * L2 for PP0-3 at 0x01000 + * L2 for PP4-7 at 0x11000 (optional) + */ + + _mali_osk_resource_t l2_gp_resource; + _mali_osk_resource_t l2_pp_grp0_resource; + _mali_osk_resource_t l2_pp_grp1_resource; + + /* Make cluster for GP's L2 */ + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x10000, &l2_gp_resource)) { + MALI_DEBUG_PRINT(3, ("Creating Mali-450 L2 cache core for GP\n")); + l2_cache = mali_create_l2_cache_core(&l2_gp_resource); + if (NULL == l2_cache) { + return _MALI_OSK_ERR_FAULT; + } + mali_pm_domain_add_l2(mali_pmu_get_domain_mask(MALI_L20_DOMAIN_INDEX), l2_cache); + } else { + MALI_DEBUG_PRINT(3, ("Did not find required Mali L2 cache for GP in config file\n")); + return _MALI_OSK_ERR_FAULT; + } + + /* Find corresponding l2 domain */ + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x1000, &l2_pp_grp0_resource)) { + MALI_DEBUG_PRINT(3, ("Creating Mali-450 L2 cache core for PP group 0\n")); + l2_cache = mali_create_l2_cache_core(&l2_pp_grp0_resource); + if (NULL == l2_cache) { + return _MALI_OSK_ERR_FAULT; + } + mali_pm_domain_add_l2(mali_pmu_get_domain_mask(MALI_L21_DOMAIN_INDEX), l2_cache); + } else { + MALI_DEBUG_PRINT(3, ("Did not find required Mali L2 cache for PP group 0 in config file\n")); + return _MALI_OSK_ERR_FAULT; + } + + /* Second PP core group is optional, don't fail if we don't find it */ + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x11000, &l2_pp_grp1_resource)) { + MALI_DEBUG_PRINT(3, ("Creating Mali-450 L2 cache core for PP group 1\n")); + l2_cache = mali_create_l2_cache_core(&l2_pp_grp1_resource); + if (NULL == l2_cache) { + return _MALI_OSK_ERR_FAULT; + } + mali_pm_domain_add_l2(mali_pmu_get_domain_mask(MALI_L22_DOMAIN_INDEX), l2_cache); + } + } + + return _MALI_OSK_ERR_OK; +} + +static struct mali_group *mali_create_group(struct mali_l2_cache_core *cache, + _mali_osk_resource_t *resource_mmu, + _mali_osk_resource_t *resource_gp, + _mali_osk_resource_t *resource_pp) +{ + struct mali_mmu_core *mmu; + struct mali_group *group; + + MALI_DEBUG_PRINT(3, ("Starting new group for MMU %s\n", resource_mmu->description)); + + /* Create the group object */ + group = mali_group_create(cache, NULL, NULL); + if (NULL == group) { + MALI_PRINT_ERROR(("Failed to create group object for MMU %s\n", resource_mmu->description)); + return NULL; + } + + /* Create the MMU object inside group */ + mmu = mali_mmu_create(resource_mmu, group, MALI_FALSE); + if (NULL == mmu) { + MALI_PRINT_ERROR(("Failed to create MMU object\n")); + mali_group_delete(group); + return NULL; + } + + if (NULL != resource_gp) { + /* Create the GP core object inside this group */ + struct mali_gp_core *gp_core = mali_gp_create(resource_gp, group); + if (NULL == gp_core) { + /* No need to clean up now, as we will clean up everything linked in from the cluster when we fail this function */ + MALI_PRINT_ERROR(("Failed to create GP object\n")); + mali_group_delete(group); + return NULL; + } + } + + if (NULL != resource_pp) { + struct mali_pp_core *pp_core; + + /* Create the PP core object inside this group */ + pp_core = mali_pp_create(resource_pp, group, MALI_FALSE, mali_get_bcast_id(resource_pp)); + if (NULL == pp_core) { + /* No need to clean up now, as we will clean up everything linked in from the cluster when we fail this function */ + MALI_PRINT_ERROR(("Failed to create PP object\n")); + mali_group_delete(group); + return NULL; + } + } + + /* Reset group */ + mali_group_lock(group); + mali_group_reset(group); + mali_group_unlock(group); + + return group; +} + +static _mali_osk_errcode_t mali_create_virtual_group(_mali_osk_resource_t *resource_mmu_pp_bcast, + _mali_osk_resource_t *resource_pp_bcast, + _mali_osk_resource_t *resource_dlbu, + _mali_osk_resource_t *resource_bcast) +{ + struct mali_mmu_core *mmu_pp_bcast_core; + struct mali_pp_core *pp_bcast_core; + struct mali_dlbu_core *dlbu_core; + struct mali_bcast_unit *bcast_core; + struct mali_group *group; + + MALI_DEBUG_PRINT(2, ("Starting new virtual group for MMU PP broadcast core %s\n", resource_mmu_pp_bcast->description)); + + /* Create the DLBU core object */ + dlbu_core = mali_dlbu_create(resource_dlbu); + if (NULL == dlbu_core) { + MALI_PRINT_ERROR(("Failed to create DLBU object \n")); + return _MALI_OSK_ERR_FAULT; + } + + /* Create the Broadcast unit core */ + bcast_core = mali_bcast_unit_create(resource_bcast); + if (NULL == bcast_core) { + MALI_PRINT_ERROR(("Failed to create Broadcast unit object!\n")); + mali_dlbu_delete(dlbu_core); + return _MALI_OSK_ERR_FAULT; + } + + /* Create the group object */ + group = mali_group_create(NULL, dlbu_core, bcast_core); + if (NULL == group) { + MALI_PRINT_ERROR(("Failed to create group object for MMU PP broadcast core %s\n", resource_mmu_pp_bcast->description)); + mali_bcast_unit_delete(bcast_core); + mali_dlbu_delete(dlbu_core); + return _MALI_OSK_ERR_FAULT; + } + + /* Create the MMU object inside group */ + mmu_pp_bcast_core = mali_mmu_create(resource_mmu_pp_bcast, group, MALI_TRUE); + if (NULL == mmu_pp_bcast_core) { + MALI_PRINT_ERROR(("Failed to create MMU PP broadcast object\n")); + mali_group_delete(group); + return _MALI_OSK_ERR_FAULT; + } + + /* Create the PP core object inside this group */ + pp_bcast_core = mali_pp_create(resource_pp_bcast, group, MALI_TRUE, 0); + if (NULL == pp_bcast_core) { + /* No need to clean up now, as we will clean up everything linked in from the cluster when we fail this function */ + MALI_PRINT_ERROR(("Failed to create PP object\n")); + mali_group_delete(group); + return _MALI_OSK_ERR_FAULT; + } + + return _MALI_OSK_ERR_OK; +} + +static _mali_osk_errcode_t mali_parse_config_groups(void) +{ + struct mali_group *group; + int cluster_id_gp = 0; + int cluster_id_pp_grp0 = 0; + int cluster_id_pp_grp1 = 0; + int i; + + _mali_osk_resource_t resource_gp; + _mali_osk_resource_t resource_gp_mmu; + _mali_osk_resource_t resource_pp[8]; + _mali_osk_resource_t resource_pp_mmu[8]; + _mali_osk_resource_t resource_pp_mmu_bcast; + _mali_osk_resource_t resource_pp_bcast; + _mali_osk_resource_t resource_dlbu; + _mali_osk_resource_t resource_bcast; + _mali_osk_errcode_t resource_gp_found; + _mali_osk_errcode_t resource_gp_mmu_found; + _mali_osk_errcode_t resource_pp_found[8]; + _mali_osk_errcode_t resource_pp_mmu_found[8]; + _mali_osk_errcode_t resource_pp_mmu_bcast_found; + _mali_osk_errcode_t resource_pp_bcast_found; + _mali_osk_errcode_t resource_dlbu_found; + _mali_osk_errcode_t resource_bcast_found; + + if (!(mali_is_mali400() || mali_is_mali450())) { + /* No known HW core */ + return _MALI_OSK_ERR_FAULT; + } + + if (MALI_MAX_JOB_RUNTIME_DEFAULT == mali_max_job_runtime) { + /* Group settings are not overridden by module parameters, so use device settings */ + struct _mali_osk_device_data data = { 0, }; + + if (_MALI_OSK_ERR_OK == _mali_osk_device_data_get(&data)) { + /* Use device specific settings (if defined) */ + if (0 != data.max_job_runtime) { + mali_max_job_runtime = data.max_job_runtime; + } + } + } + + if (mali_is_mali450()) { + /* Mali-450 have separate L2s for GP, and PP core group(s) */ + cluster_id_pp_grp0 = 1; + cluster_id_pp_grp1 = 2; + } + + resource_gp_found = _mali_osk_resource_find(global_gpu_base_address + 0x00000, &resource_gp); + resource_gp_mmu_found = _mali_osk_resource_find(global_gpu_base_address + 0x03000, &resource_gp_mmu); + resource_pp_found[0] = _mali_osk_resource_find(global_gpu_base_address + 0x08000, &(resource_pp[0])); + resource_pp_found[1] = _mali_osk_resource_find(global_gpu_base_address + 0x0A000, &(resource_pp[1])); + resource_pp_found[2] = _mali_osk_resource_find(global_gpu_base_address + 0x0C000, &(resource_pp[2])); + resource_pp_found[3] = _mali_osk_resource_find(global_gpu_base_address + 0x0E000, &(resource_pp[3])); + resource_pp_found[4] = _mali_osk_resource_find(global_gpu_base_address + 0x28000, &(resource_pp[4])); + resource_pp_found[5] = _mali_osk_resource_find(global_gpu_base_address + 0x2A000, &(resource_pp[5])); + resource_pp_found[6] = _mali_osk_resource_find(global_gpu_base_address + 0x2C000, &(resource_pp[6])); + resource_pp_found[7] = _mali_osk_resource_find(global_gpu_base_address + 0x2E000, &(resource_pp[7])); + resource_pp_mmu_found[0] = _mali_osk_resource_find(global_gpu_base_address + 0x04000, &(resource_pp_mmu[0])); + resource_pp_mmu_found[1] = _mali_osk_resource_find(global_gpu_base_address + 0x05000, &(resource_pp_mmu[1])); + resource_pp_mmu_found[2] = _mali_osk_resource_find(global_gpu_base_address + 0x06000, &(resource_pp_mmu[2])); + resource_pp_mmu_found[3] = _mali_osk_resource_find(global_gpu_base_address + 0x07000, &(resource_pp_mmu[3])); + resource_pp_mmu_found[4] = _mali_osk_resource_find(global_gpu_base_address + 0x1C000, &(resource_pp_mmu[4])); + resource_pp_mmu_found[5] = _mali_osk_resource_find(global_gpu_base_address + 0x1D000, &(resource_pp_mmu[5])); + resource_pp_mmu_found[6] = _mali_osk_resource_find(global_gpu_base_address + 0x1E000, &(resource_pp_mmu[6])); + resource_pp_mmu_found[7] = _mali_osk_resource_find(global_gpu_base_address + 0x1F000, &(resource_pp_mmu[7])); + + + if (mali_is_mali450()) { + resource_bcast_found = _mali_osk_resource_find(global_gpu_base_address + 0x13000, &resource_bcast); + resource_dlbu_found = _mali_osk_resource_find(global_gpu_base_address + 0x14000, &resource_dlbu); + resource_pp_mmu_bcast_found = _mali_osk_resource_find(global_gpu_base_address + 0x15000, &resource_pp_mmu_bcast); + resource_pp_bcast_found = _mali_osk_resource_find(global_gpu_base_address + 0x16000, &resource_pp_bcast); + + if (_MALI_OSK_ERR_OK != resource_bcast_found || + _MALI_OSK_ERR_OK != resource_dlbu_found || + _MALI_OSK_ERR_OK != resource_pp_mmu_bcast_found || + _MALI_OSK_ERR_OK != resource_pp_bcast_found) { + /* Missing mandatory core(s) for Mali-450 */ + MALI_DEBUG_PRINT(2, ("Missing mandatory resources, Mali-450 needs DLBU, Broadcast unit, virtual PP core and virtual MMU\n")); + return _MALI_OSK_ERR_FAULT; + } + } + + if (_MALI_OSK_ERR_OK != resource_gp_found || + _MALI_OSK_ERR_OK != resource_gp_mmu_found || + _MALI_OSK_ERR_OK != resource_pp_found[0] || + _MALI_OSK_ERR_OK != resource_pp_mmu_found[0]) { + /* Missing mandatory core(s) */ + MALI_DEBUG_PRINT(2, ("Missing mandatory resource, need at least one GP and one PP, both with a separate MMU\n")); + return _MALI_OSK_ERR_FAULT; + } + + MALI_DEBUG_ASSERT(1 <= mali_l2_cache_core_get_glob_num_l2_cores()); + group = mali_create_group(mali_l2_cache_core_get_glob_l2_core(cluster_id_gp), &resource_gp_mmu, &resource_gp, NULL); + if (NULL == group) { + return _MALI_OSK_ERR_FAULT; + } + + /* Add GP in group, for PMU ref count */ + mali_pm_domain_add_group(mali_pmu_get_domain_mask(MALI_GP_DOMAIN_INDEX), group); + + /* Create group for first (and mandatory) PP core */ + MALI_DEBUG_ASSERT(mali_l2_cache_core_get_glob_num_l2_cores() >= (cluster_id_pp_grp0 + 1)); /* >= 1 on Mali-300 and Mali-400, >= 2 on Mali-450 */ + group = mali_create_group(mali_l2_cache_core_get_glob_l2_core(cluster_id_pp_grp0), &resource_pp_mmu[0], NULL, &resource_pp[0]); + if (NULL == group) { + return _MALI_OSK_ERR_FAULT; + } + + /* Find corresponding pp domain */ + mali_pm_domain_add_group(mali_pmu_get_domain_mask(MALI_PP0_DOMAIN_INDEX), group); + + mali_inited_pp_cores_group_1++; + + /* Create groups for rest of the cores in the first PP core group */ + for (i = 1; i < 4; i++) { /* First half of the PP cores belong to first core group */ + if (mali_inited_pp_cores_group_1 < mali_max_pp_cores_group_1) { + if (_MALI_OSK_ERR_OK == resource_pp_found[i] && _MALI_OSK_ERR_OK == resource_pp_mmu_found[i]) { + group = mali_create_group(mali_l2_cache_core_get_glob_l2_core(cluster_id_pp_grp0), &resource_pp_mmu[i], NULL, &resource_pp[i]); + if (NULL == group) { + return _MALI_OSK_ERR_FAULT; + } + + mali_pm_domain_add_group(mali_pmu_get_domain_mask(i + MALI_PP0_DOMAIN_INDEX), group); + + mali_inited_pp_cores_group_1++; + } + } + } + + /* Create groups for cores in the second PP core group */ + for (i = 4; i < 8; i++) { /* Second half of the PP cores belong to second core group */ + if (mali_inited_pp_cores_group_2 < mali_max_pp_cores_group_2) { + if (_MALI_OSK_ERR_OK == resource_pp_found[i] && _MALI_OSK_ERR_OK == resource_pp_mmu_found[i]) { + MALI_DEBUG_ASSERT(mali_l2_cache_core_get_glob_num_l2_cores() >= 2); /* Only Mali-450 have a second core group */ + group = mali_create_group(mali_l2_cache_core_get_glob_l2_core(cluster_id_pp_grp1), &resource_pp_mmu[i], NULL, &resource_pp[i]); + if (NULL == group) { + return _MALI_OSK_ERR_FAULT; + } + mali_pm_domain_add_group(mali_pmu_get_domain_mask(i + MALI_PP0_DOMAIN_INDEX), group); + mali_inited_pp_cores_group_2++; + } + } + } + + if(mali_is_mali450()) { + _mali_osk_errcode_t err = mali_create_virtual_group(&resource_pp_mmu_bcast, &resource_pp_bcast, &resource_dlbu, &resource_bcast); + if (_MALI_OSK_ERR_OK != err) { + return err; + } + } + + mali_max_pp_cores_group_1 = mali_inited_pp_cores_group_1; + mali_max_pp_cores_group_2 = mali_inited_pp_cores_group_2; + MALI_DEBUG_PRINT(2, ("%d+%d PP cores initialized\n", mali_inited_pp_cores_group_1, mali_inited_pp_cores_group_2)); + + return _MALI_OSK_ERR_OK; +} + +static _mali_osk_errcode_t mali_check_shared_interrupts(void) +{ +#if !defined(CONFIG_MALI_SHARED_INTERRUPTS) + if (MALI_TRUE == _mali_osk_shared_interrupts()) { + MALI_PRINT_ERROR(("Shared interrupts detected, but driver support is not enabled\n")); + return _MALI_OSK_ERR_FAULT; + } +#endif /* !defined(CONFIG_MALI_SHARED_INTERRUPTS) */ + + /* It is OK to compile support for shared interrupts even if Mali is not using it. */ + return _MALI_OSK_ERR_OK; +} + +static _mali_osk_errcode_t mali_create_pm_domains(void) +{ + int i; + + for (i = 0; i < MALI_MAX_NUMBER_OF_DOMAINS; i++) { + if (0x0 == mali_pmu_get_domain_mask(i)) continue; + + if (NULL == mali_pm_domain_create(mali_pmu_get_domain_mask(i))) { + return _MALI_OSK_ERR_NOMEM; + } + } + + return _MALI_OSK_ERR_OK; +} + +static void mali_use_default_pm_domain_config(void) +{ + u32 pp_count_gr1 = 0; + u32 pp_count_gr2 = 0; + u32 l2_count = 0; + + MALI_DEBUG_ASSERT(0 != global_gpu_base_address); + + /* GP core */ + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x00000, NULL)) { + mali_pmu_set_domain_mask(MALI_GP_DOMAIN_INDEX, 0x01); + } + + /* PP0 - PP3 core */ + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x08000, NULL)) { + ++pp_count_gr1; + + if (mali_is_mali400()) { + mali_pmu_set_domain_mask(MALI_PP0_DOMAIN_INDEX, 0x01<<2); + } else if (mali_is_mali450()) { + mali_pmu_set_domain_mask(MALI_PP0_DOMAIN_INDEX, 0x01<<1); + } + } + + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x0A000, NULL)) { + ++pp_count_gr1; + + if (mali_is_mali400()) { + mali_pmu_set_domain_mask(MALI_PP1_DOMAIN_INDEX, 0x01<<3); + } else if (mali_is_mali450()) { + mali_pmu_set_domain_mask(MALI_PP1_DOMAIN_INDEX, 0x01<<2); + } + } + + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x0C000, NULL)) { + ++pp_count_gr1; + + if (mali_is_mali400()) { + mali_pmu_set_domain_mask(MALI_PP2_DOMAIN_INDEX, 0x01<<4); + } else if (mali_is_mali450()) { + mali_pmu_set_domain_mask(MALI_PP2_DOMAIN_INDEX, 0x01<<2); + } + } + + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x0E000, NULL)) { + ++pp_count_gr1; + + if (mali_is_mali400()) { + mali_pmu_set_domain_mask(MALI_PP3_DOMAIN_INDEX, 0x01<<5); + } else if (mali_is_mali450()) { + mali_pmu_set_domain_mask(MALI_PP3_DOMAIN_INDEX, 0x01<<2); + } + } + + /* PP4 - PP7 */ + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x28000, NULL)) { + ++pp_count_gr2; + + mali_pmu_set_domain_mask(MALI_PP4_DOMAIN_INDEX, 0x01<<3); + } + + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x2A000, NULL)) { + ++pp_count_gr2; + + mali_pmu_set_domain_mask(MALI_PP5_DOMAIN_INDEX, 0x01<<3); + } + + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x2C000, NULL)) { + ++pp_count_gr2; + + mali_pmu_set_domain_mask(MALI_PP6_DOMAIN_INDEX, 0x01<<3); + } + + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x2E000, NULL)) { + ++pp_count_gr2; + + mali_pmu_set_domain_mask(MALI_PP7_DOMAIN_INDEX, 0x01<<3); + } + + /* L2gp/L2PP0/L2PP4 */ + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x10000, NULL)) { + ++l2_count; + + if (mali_is_mali400()) { + mali_pmu_set_domain_mask(MALI_L20_DOMAIN_INDEX, 0x01<<1); + } else if (mali_is_mali450()) { + mali_pmu_set_domain_mask(MALI_L20_DOMAIN_INDEX, 0x01<<0); + } + } + + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x1000, NULL)) { + ++l2_count; + + mali_pmu_set_domain_mask(MALI_L21_DOMAIN_INDEX, 0x01<<1); + } + + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x11000, NULL)) { + ++l2_count; + + mali_pmu_set_domain_mask(MALI_L22_DOMAIN_INDEX, 0x01<<3); + } + + MALI_DEBUG_PRINT(2, ("Using default PMU domain config: (%d) gr1_pp_cores, (%d) gr2_pp_cores, (%d) l2_count. \n", pp_count_gr1, pp_count_gr2, l2_count)); +} + +static void mali_set_pmu_global_domain_config(void) +{ + struct _mali_osk_device_data data = { 0, }; + int i = 0; + + if (_MALI_OSK_ERR_OK == _mali_osk_device_data_get(&data)) { + /* Check whether has customized pmu domain configure */ + for (i = 0; i < MALI_MAX_NUMBER_OF_DOMAINS; i++) { + if (0 != data.pmu_domain_config[i]) break; + } + + if (MALI_MAX_NUMBER_OF_DOMAINS == i) { + mali_use_default_pm_domain_config(); + } else { + /* Copy the customer config to global config */ + mali_pmu_copy_domain_mask(data.pmu_domain_config, sizeof(data.pmu_domain_config)); + } + } +} + +static _mali_osk_errcode_t mali_parse_config_pmu(void) +{ + _mali_osk_resource_t resource_pmu; + + MALI_DEBUG_ASSERT(0 != global_gpu_base_address); + + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x02000, &resource_pmu)) { + struct mali_pmu_core *pmu; + + mali_set_pmu_global_domain_config(); + + pmu = mali_pmu_create(&resource_pmu); + if (NULL == pmu) { + MALI_PRINT_ERROR(("Failed to create PMU\n")); + return _MALI_OSK_ERR_FAULT; + } + } + + /* It's ok if the PMU doesn't exist */ + return _MALI_OSK_ERR_OK; +} + +static _mali_osk_errcode_t mali_parse_config_dma(void) +{ + _mali_osk_resource_t resource_dma; + + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x12000, &resource_dma)) { + if (NULL == mali_dma_create(&resource_dma)) { + return _MALI_OSK_ERR_FAULT; + } + return _MALI_OSK_ERR_OK; + } else { + return _MALI_OSK_ERR_ITEM_NOT_FOUND; + } +} + +static _mali_osk_errcode_t mali_parse_config_memory(void) +{ + _mali_osk_errcode_t ret; + + if (0 == mali_dedicated_mem_start && 0 == mali_dedicated_mem_size && 0 == mali_shared_mem_size) { + /* Memory settings are not overridden by module parameters, so use device settings */ + struct _mali_osk_device_data data = { 0, }; + + if (_MALI_OSK_ERR_OK == _mali_osk_device_data_get(&data)) { + /* Use device specific settings (if defined) */ + mali_dedicated_mem_start = data.dedicated_mem_start; + mali_dedicated_mem_size = data.dedicated_mem_size; + mali_shared_mem_size = data.shared_mem_size; + } + + if (0 == mali_dedicated_mem_start && 0 == mali_dedicated_mem_size && 0 == mali_shared_mem_size) { + /* No GPU memory specified */ + return _MALI_OSK_ERR_INVALID_ARGS; + } + + MALI_DEBUG_PRINT(2, ("Using device defined memory settings (dedicated: 0x%08X@0x%08X, shared: 0x%08X)\n", + mali_dedicated_mem_size, mali_dedicated_mem_start, mali_shared_mem_size)); + } else { + MALI_DEBUG_PRINT(2, ("Using module defined memory settings (dedicated: 0x%08X@0x%08X, shared: 0x%08X)\n", + mali_dedicated_mem_size, mali_dedicated_mem_start, mali_shared_mem_size)); + } + + if (0 < mali_dedicated_mem_size && 0 != mali_dedicated_mem_start) { + /* Dedicated memory */ + ret = mali_memory_core_resource_dedicated_memory(mali_dedicated_mem_start, mali_dedicated_mem_size); + if (_MALI_OSK_ERR_OK != ret) { + MALI_PRINT_ERROR(("Failed to register dedicated memory\n")); + mali_memory_terminate(); + return ret; + } + } + + if (0 < mali_shared_mem_size) { + /* Shared OS memory */ + ret = mali_memory_core_resource_os_memory(mali_shared_mem_size); + if (_MALI_OSK_ERR_OK != ret) { + MALI_PRINT_ERROR(("Failed to register shared OS memory\n")); + mali_memory_terminate(); + return ret; + } + } + + if (0 == mali_fb_start && 0 == mali_fb_size) { + /* Frame buffer settings are not overridden by module parameters, so use device settings */ + struct _mali_osk_device_data data = { 0, }; + + if (_MALI_OSK_ERR_OK == _mali_osk_device_data_get(&data)) { + /* Use device specific settings (if defined) */ + mali_fb_start = data.fb_start; + mali_fb_size = data.fb_size; + } + + MALI_DEBUG_PRINT(2, ("Using device defined frame buffer settings (0x%08X@0x%08X)\n", + mali_fb_size, mali_fb_start)); + } else { + MALI_DEBUG_PRINT(2, ("Using module defined frame buffer settings (0x%08X@0x%08X)\n", + mali_fb_size, mali_fb_start)); + } + + if (0 != mali_fb_size) { + /* Register frame buffer */ + ret = mali_mem_validation_add_range(mali_fb_start, mali_fb_size); + if (_MALI_OSK_ERR_OK != ret) { + MALI_PRINT_ERROR(("Failed to register frame buffer memory region\n")); + mali_memory_terminate(); + return ret; + } + } + + return _MALI_OSK_ERR_OK; +} + +static void mali_detect_gpu_class(void) +{ + u32 number_of_pp_cores = 0; + u32 number_of_l2_caches = 0; + + mali_resource_count(&number_of_pp_cores, &number_of_l2_caches); + if (number_of_l2_caches > 1) { + mali_gpu_class_is_mali450 = MALI_TRUE; + } +} + +_mali_osk_errcode_t mali_initialize_subsystems(void) +{ + _mali_osk_errcode_t err; + struct mali_pmu_core *pmu; + + mali_pp_job_initialize(); + + err = mali_session_initialize(); + if (_MALI_OSK_ERR_OK != err) goto session_init_failed; + +#if defined(CONFIG_MALI400_PROFILING) + err = _mali_osk_profiling_init(mali_boot_profiling ? MALI_TRUE : MALI_FALSE); + if (_MALI_OSK_ERR_OK != err) { + /* No biggie if we weren't able to initialize the profiling */ + MALI_PRINT_ERROR(("Failed to initialize profiling, feature will be unavailable\n")); + } +#endif + + err = mali_memory_initialize(); + if (_MALI_OSK_ERR_OK != err) goto memory_init_failed; + + /* Configure memory early. Memory allocation needed for mali_mmu_initialize. */ + err = mali_parse_config_memory(); + if (_MALI_OSK_ERR_OK != err) goto parse_memory_config_failed; + + err = mali_set_global_gpu_base_address(); + if (_MALI_OSK_ERR_OK != err) goto set_global_gpu_base_address_failed; + + /* Detect gpu class according to l2 cache number */ + mali_detect_gpu_class(); + + err = mali_check_shared_interrupts(); + if (_MALI_OSK_ERR_OK != err) goto check_shared_interrupts_failed; + + err = mali_pp_scheduler_initialize(); + if (_MALI_OSK_ERR_OK != err) goto pp_scheduler_init_failed; + + /* Initialize the power management module */ + err = mali_pm_initialize(); + if (_MALI_OSK_ERR_OK != err) goto pm_init_failed; + + /* Initialize the MALI PMU */ + err = mali_parse_config_pmu(); + if (_MALI_OSK_ERR_OK != err) goto parse_pmu_config_failed; + + /* Make sure the power stays on for the rest of this function */ + err = _mali_osk_pm_dev_ref_add(); + if (_MALI_OSK_ERR_OK != err) goto pm_always_on_failed; + + /* + * If run-time PM is used, then the mali_pm module has now already been + * notified that the power now is on (through the resume callback functions). + * However, if run-time PM is not used, then there will probably not be any + * calls to the resume callback functions, so we need to explicitly tell it + * that the power is on. + */ + mali_pm_set_power_is_on(); + + /* Reset PMU HW and ensure all Mali power domains are on */ + pmu = mali_pmu_get_global_pmu_core(); + if (NULL != pmu) { + err = mali_pmu_reset(pmu); + if (_MALI_OSK_ERR_OK != err) goto pmu_reset_failed; + } + + /* Detect which Mali GPU we are dealing with */ + err = mali_parse_product_info(); + if (_MALI_OSK_ERR_OK != err) goto product_info_parsing_failed; + + /* The global_product_id is now populated with the correct Mali GPU */ + + /* Create PM domains only if PMU exists */ + if (NULL != pmu) { + err = mali_create_pm_domains(); + if (_MALI_OSK_ERR_OK != err) goto pm_domain_failed; + } + + /* Initialize MMU module */ + err = mali_mmu_initialize(); + if (_MALI_OSK_ERR_OK != err) goto mmu_init_failed; + + if (mali_is_mali450()) { + err = mali_dlbu_initialize(); + if (_MALI_OSK_ERR_OK != err) goto dlbu_init_failed; + + err = mali_parse_config_dma(); + if (_MALI_OSK_ERR_OK != err) goto dma_parsing_failed; + } + + /* Start configuring the actual Mali hardware. */ + err = mali_parse_config_l2_cache(); + if (_MALI_OSK_ERR_OK != err) goto config_parsing_failed; + err = mali_parse_config_groups(); + if (_MALI_OSK_ERR_OK != err) goto config_parsing_failed; + + /* Initialize the schedulers */ + err = mali_scheduler_initialize(); + if (_MALI_OSK_ERR_OK != err) goto scheduler_init_failed; + err = mali_gp_scheduler_initialize(); + if (_MALI_OSK_ERR_OK != err) goto gp_scheduler_init_failed; + + /* PP scheduler population can't fail */ + mali_pp_scheduler_populate(); + + /* Initialize the GPU utilization tracking */ + err = mali_utilization_init(); + if (_MALI_OSK_ERR_OK != err) goto utilization_init_failed; + + /* Allowing the system to be turned off */ + _mali_osk_pm_dev_ref_dec(); + + MALI_SUCCESS; /* all ok */ + + /* Error handling */ + +utilization_init_failed: + mali_pp_scheduler_depopulate(); + mali_gp_scheduler_terminate(); +gp_scheduler_init_failed: + mali_scheduler_terminate(); +scheduler_init_failed: +config_parsing_failed: + mali_delete_groups(); /* Delete any groups not (yet) owned by a scheduler */ + mali_delete_l2_cache_cores(); /* Delete L2 cache cores even if config parsing failed. */ + { + struct mali_dma_core *dma = mali_dma_get_global_dma_core(); + if (NULL != dma) mali_dma_delete(dma); + } +dma_parsing_failed: + mali_dlbu_terminate(); +dlbu_init_failed: + mali_mmu_terminate(); +mmu_init_failed: + mali_pm_domain_terminate(); +pm_domain_failed: + /* Nothing to roll back */ +product_info_parsing_failed: + /* Nothing to roll back */ +pmu_reset_failed: + /* Allowing the system to be turned off */ + _mali_osk_pm_dev_ref_dec(); +pm_always_on_failed: + pmu = mali_pmu_get_global_pmu_core(); + if (NULL != pmu) { + mali_pmu_delete(pmu); + } +parse_pmu_config_failed: + mali_pm_terminate(); +pm_init_failed: + mali_pp_scheduler_terminate(); +pp_scheduler_init_failed: +check_shared_interrupts_failed: + global_gpu_base_address = 0; +set_global_gpu_base_address_failed: + /* undoing mali_parse_config_memory() is done by mali_memory_terminate() */ +parse_memory_config_failed: + mali_memory_terminate(); +memory_init_failed: +#if defined(CONFIG_MALI400_PROFILING) + _mali_osk_profiling_term(); +#endif + mali_session_terminate(); +session_init_failed: + mali_pp_job_terminate(); + return err; +} + +void mali_terminate_subsystems(void) +{ + struct mali_pmu_core *pmu = mali_pmu_get_global_pmu_core(); + struct mali_dma_core *dma = mali_dma_get_global_dma_core(); + + MALI_DEBUG_PRINT(2, ("terminate_subsystems() called\n")); + + /* shut down subsystems in reverse order from startup */ + + /* We need the GPU to be powered up for the terminate sequence */ + _mali_osk_pm_dev_ref_add(); + + mali_utilization_term(); + mali_pp_scheduler_depopulate(); + mali_gp_scheduler_terminate(); + mali_scheduler_terminate(); + mali_delete_l2_cache_cores(); + if (mali_is_mali450()) { + mali_dlbu_terminate(); + } + mali_mmu_terminate(); + if (NULL != pmu) { + mali_pmu_delete(pmu); + } + if (NULL != dma) { + mali_dma_delete(dma); + } + mali_pm_terminate(); + mali_memory_terminate(); +#if defined(CONFIG_MALI400_PROFILING) + _mali_osk_profiling_term(); +#endif + + /* Allowing the system to be turned off */ + _mali_osk_pm_dev_ref_dec(); + + mali_pp_scheduler_terminate(); + mali_session_terminate(); + + mali_pp_job_terminate(); +} + +_mali_product_id_t mali_kernel_core_get_product_id(void) +{ + return global_product_id; +} + +u32 mali_kernel_core_get_gpu_major_version(void) +{ + return global_gpu_major_version; +} + +u32 mali_kernel_core_get_gpu_minor_version(void) +{ + return global_gpu_minor_version; +} + +_mali_osk_errcode_t _mali_ukk_get_api_version( _mali_uk_get_api_version_s *args ) +{ + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + + /* check compatability */ + if ( args->version == _MALI_UK_API_VERSION ) { + args->compatible = 1; + } else { + args->compatible = 0; + } + + args->version = _MALI_UK_API_VERSION; /* report our version */ + + /* success regardless of being compatible or not */ + MALI_SUCCESS; +} + +_mali_osk_errcode_t _mali_ukk_wait_for_notification( _mali_uk_wait_for_notification_s *args ) +{ + _mali_osk_errcode_t err; + _mali_osk_notification_t * notification; + _mali_osk_notification_queue_t *queue; + + /* check input */ + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + + queue = ((struct mali_session_data *)args->ctx)->ioctl_queue; + + /* if the queue does not exist we're currently shutting down */ + if (NULL == queue) { + MALI_DEBUG_PRINT(1, ("No notification queue registered with the session. Asking userspace to stop querying\n")); + args->type = _MALI_NOTIFICATION_CORE_SHUTDOWN_IN_PROGRESS; + MALI_SUCCESS; + } + + /* receive a notification, might sleep */ + err = _mali_osk_notification_queue_receive(queue, ¬ification); + if (_MALI_OSK_ERR_OK != err) { + MALI_ERROR(err); /* errcode returned, pass on to caller */ + } + + /* copy the buffer to the user */ + args->type = (_mali_uk_notification_type)notification->notification_type; + _mali_osk_memcpy(&args->data, notification->result_buffer, notification->result_buffer_size); + + /* finished with the notification */ + _mali_osk_notification_delete( notification ); + + MALI_SUCCESS; /* all ok */ +} + +_mali_osk_errcode_t _mali_ukk_post_notification( _mali_uk_post_notification_s *args ) +{ + _mali_osk_notification_t * notification; + _mali_osk_notification_queue_t *queue; + + /* check input */ + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + + queue = ((struct mali_session_data *)args->ctx)->ioctl_queue; + + /* if the queue does not exist we're currently shutting down */ + if (NULL == queue) { + MALI_DEBUG_PRINT(1, ("No notification queue registered with the session. Asking userspace to stop querying\n")); + MALI_SUCCESS; + } + + notification = _mali_osk_notification_create(args->type, 0); + if (NULL == notification) { + MALI_PRINT_ERROR( ("Failed to create notification object\n")); + return _MALI_OSK_ERR_NOMEM; + } + + _mali_osk_notification_queue_send(queue, notification); + + MALI_SUCCESS; /* all ok */ +} + +_mali_osk_errcode_t _mali_ukk_request_high_priority( _mali_uk_request_high_priority_s *args ) +{ + struct mali_session_data *session; + + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + + session = (struct mali_session_data *) args->ctx; + + if (!session->use_high_priority_job_queue) { + session->use_high_priority_job_queue = MALI_TRUE; + MALI_DEBUG_PRINT(2, ("Session 0x%08X with pid %d was granted higher priority.\n", session, _mali_osk_get_pid())); + } + + MALI_SUCCESS; +} + +_mali_osk_errcode_t _mali_ukk_open(void **context) +{ + u32 i; + struct mali_session_data *session; + + /* allocated struct to track this session */ + session = (struct mali_session_data *)_mali_osk_calloc(1, sizeof(struct mali_session_data)); + MALI_CHECK_NON_NULL(session, _MALI_OSK_ERR_NOMEM); + + MALI_DEBUG_PRINT(3, ("Session starting\n")); + + /* create a response queue for this session */ + session->ioctl_queue = _mali_osk_notification_queue_init(); + if (NULL == session->ioctl_queue) { + _mali_osk_free(session); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + session->page_directory = mali_mmu_pagedir_alloc(); + if (NULL == session->page_directory) { + _mali_osk_notification_queue_term(session->ioctl_queue); + _mali_osk_free(session); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + if (_MALI_OSK_ERR_OK != mali_mmu_pagedir_map(session->page_directory, MALI_DLBU_VIRT_ADDR, _MALI_OSK_MALI_PAGE_SIZE)) { + MALI_PRINT_ERROR(("Failed to map DLBU page into session\n")); + _mali_osk_notification_queue_term(session->ioctl_queue); + _mali_osk_free(session); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + if (0 != mali_dlbu_phys_addr) { + mali_mmu_pagedir_update(session->page_directory, MALI_DLBU_VIRT_ADDR, mali_dlbu_phys_addr, + _MALI_OSK_MALI_PAGE_SIZE, MALI_MMU_FLAGS_DEFAULT); + } + + if (_MALI_OSK_ERR_OK != mali_memory_session_begin(session)) { + mali_mmu_pagedir_free(session->page_directory); + _mali_osk_notification_queue_term(session->ioctl_queue); + _mali_osk_free(session); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + /* Create soft system. */ + session->soft_job_system = mali_soft_job_system_create(session); + if (NULL == session->soft_job_system) { + mali_memory_session_end(session); + mali_mmu_pagedir_free(session->page_directory); + _mali_osk_notification_queue_term(session->ioctl_queue); + _mali_osk_free(session); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + /* Create timeline system. */ + session->timeline_system = mali_timeline_system_create(session); + if (NULL == session->timeline_system) { + mali_soft_job_system_destroy(session->soft_job_system); + mali_memory_session_end(session); + mali_mmu_pagedir_free(session->page_directory); + _mali_osk_notification_queue_term(session->ioctl_queue); + _mali_osk_free(session); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + +#if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY) + if ( _MALI_OSK_ERR_OK != _mali_osk_atomic_init(&session->number_of_window_jobs, 0)) { + MALI_DEBUG_PRINT_ERROR(("Initialization of atomic number_of_window_jobs failed.\n")); + mali_timeline_system_destroy(session->timeline_system); + mali_soft_job_system_destroy(session->soft_job_system); + mali_memory_session_end(session); + mali_mmu_pagedir_free(session->page_directory); + _mali_osk_notification_queue_term(session->ioctl_queue); + _mali_osk_free(session); + return _MALI_OSK_ERR_FAULT; + } +#endif + + session->use_high_priority_job_queue = MALI_FALSE; + + /* Initialize list of PP jobs on this session. */ + _MALI_OSK_INIT_LIST_HEAD(&session->pp_job_list); + + /* Initialize the pp_job_fb_lookup_list array used to quickly lookup jobs from a given frame builder */ + for (i = 0; i < MALI_PP_JOB_FB_LOOKUP_LIST_SIZE; ++i) { + _MALI_OSK_INIT_LIST_HEAD(&session->pp_job_fb_lookup_list[i]); + } + + *context = (void*)session; + + /* Add session to the list of all sessions. */ + mali_session_add(session); + + MALI_DEBUG_PRINT(2, ("Session started\n")); + MALI_SUCCESS; +} + +_mali_osk_errcode_t _mali_ukk_close(void **context) +{ + struct mali_session_data *session; + MALI_CHECK_NON_NULL(context, _MALI_OSK_ERR_INVALID_ARGS); + session = (struct mali_session_data *)*context; + + MALI_DEBUG_PRINT(3, ("Session ending\n")); + + MALI_DEBUG_ASSERT_POINTER(session->soft_job_system); + MALI_DEBUG_ASSERT_POINTER(session->timeline_system); + + /* Remove session from list of all sessions. */ + mali_session_remove(session); + + /* This flag is used to prevent queueing of jobs due to activation. */ + session->is_aborting = MALI_TRUE; + + /* Stop the soft job timer. */ + mali_timeline_system_stop_timer(session->timeline_system); + + /* Abort queued and running GP and PP jobs. */ + mali_gp_scheduler_abort_session(session); + mali_pp_scheduler_abort_session(session); + + /* Abort the soft job system. */ + mali_soft_job_system_abort(session->soft_job_system); + + /* Force execution of all pending bottom half processing for GP and PP. */ + _mali_osk_wq_flush(); + + /* The session PP list should now be empty. */ + MALI_DEBUG_ASSERT(_mali_osk_list_empty(&session->pp_job_list)); + + /* At this point the GP and PP scheduler no longer has any jobs queued or running from this + * session, and all soft jobs in the soft job system has been destroyed. */ + + /* Any trackers left in the timeline system are directly or indirectly waiting on external + * sync fences. Cancel all sync fence waiters to trigger activation of all remaining + * trackers. This call will sleep until all timelines are empty. */ + mali_timeline_system_abort(session->timeline_system); + + /* Flush pending work. + * Needed to make sure all bottom half processing related to this + * session has been completed, before we free internal data structures. + */ + _mali_osk_wq_flush(); + + /* Destroy timeline system. */ + mali_timeline_system_destroy(session->timeline_system); + session->timeline_system = NULL; + + /* Destroy soft system. */ + mali_soft_job_system_destroy(session->soft_job_system); + session->soft_job_system = NULL; + + MALI_DEBUG_CODE( { + /* Check that the pp_job_fb_lookup_list array is empty. */ + u32 i; + for (i = 0; i < MALI_PP_JOB_FB_LOOKUP_LIST_SIZE; ++i) + { + MALI_DEBUG_ASSERT(_mali_osk_list_empty(&session->pp_job_fb_lookup_list[i])); + } + }); + + /* Free remaining memory allocated to this session */ + mali_memory_session_end(session); + +#if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY) + _mali_osk_atomic_term(&session->number_of_window_jobs); +#endif + + /* Free session data structures */ + mali_mmu_pagedir_free(session->page_directory); + _mali_osk_notification_queue_term(session->ioctl_queue); + _mali_osk_free(session); + + *context = NULL; + + MALI_DEBUG_PRINT(2, ("Session has ended\n")); + + MALI_SUCCESS; +} + +#if MALI_STATE_TRACKING +u32 _mali_kernel_core_dump_state(char* buf, u32 size) +{ + int n = 0; /* Number of bytes written to buf */ + + n += mali_gp_scheduler_dump_state(buf + n, size - n); + n += mali_pp_scheduler_dump_state(buf + n, size - n); + + return n; +} +#endif diff --git a/drivers/gpu/arm/mali/common/mali_kernel_core.h b/drivers/gpu/arm/mali/common/mali_kernel_core.h new file mode 100644 index 00000000000000..0bbdb612c4a802 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_core.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_CORE_H__ +#define __MALI_KERNEL_CORE_H__ + +#include "mali_osk.h" + +typedef enum { + _MALI_PRODUCT_ID_UNKNOWN, + _MALI_PRODUCT_ID_MALI200, + _MALI_PRODUCT_ID_MALI300, + _MALI_PRODUCT_ID_MALI400, + _MALI_PRODUCT_ID_MALI450, +} _mali_product_id_t; + +extern mali_bool mali_gpu_class_is_mali450; + +_mali_osk_errcode_t mali_initialize_subsystems(void); + +void mali_terminate_subsystems(void); + +_mali_product_id_t mali_kernel_core_get_product_id(void); + +u32 mali_kernel_core_get_gpu_major_version(void); + +u32 mali_kernel_core_get_gpu_minor_version(void); + +u32 _mali_kernel_core_dump_state(char* buf, u32 size); + +MALI_STATIC_INLINE mali_bool mali_is_mali450(void) +{ +#if defined(CONFIG_MALI450) + return mali_gpu_class_is_mali450; +#else + return MALI_FALSE; +#endif +} + +MALI_STATIC_INLINE mali_bool mali_is_mali400(void) +{ +#if !defined(CONFIG_MALI450) + return MALI_TRUE; +#else + return !mali_gpu_class_is_mali450; +#endif +} + +#endif /* __MALI_KERNEL_CORE_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_kernel_descriptor_mapping.c b/drivers/gpu/arm/mali/common/mali_kernel_descriptor_mapping.c new file mode 100644 index 00000000000000..67b47b84168a64 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_descriptor_mapping.c @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2010, 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_kernel_descriptor_mapping.h" +#include "mali_osk.h" +#include "mali_osk_bitops.h" + +#define MALI_PAD_INT(x) (((x) + (BITS_PER_LONG - 1)) & ~(BITS_PER_LONG - 1)) + +/** + * Allocate a descriptor table capable of holding 'count' mappings + * @param count Number of mappings in the table + * @return Pointer to a new table, NULL on error + */ +static mali_descriptor_table * descriptor_table_alloc(int count); + +/** + * Free a descriptor table + * @param table The table to free + */ +static void descriptor_table_free(mali_descriptor_table * table); + +mali_descriptor_mapping * mali_descriptor_mapping_create(int init_entries, int max_entries) +{ + mali_descriptor_mapping * map = _mali_osk_calloc(1, sizeof(mali_descriptor_mapping)); + + init_entries = MALI_PAD_INT(init_entries); + max_entries = MALI_PAD_INT(max_entries); + + if (NULL != map) { + map->table = descriptor_table_alloc(init_entries); + if (NULL != map->table) { + map->lock = _mali_osk_mutex_rw_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_DESCRIPTOR_MAP); + if (NULL != map->lock) { + _mali_osk_set_nonatomic_bit(0, map->table->usage); /* reserve bit 0 to prevent NULL/zero logic to kick in */ + map->max_nr_mappings_allowed = max_entries; + map->current_nr_mappings = init_entries; + return map; + } + descriptor_table_free(map->table); + } + _mali_osk_free(map); + } + return NULL; +} + +void mali_descriptor_mapping_destroy(mali_descriptor_mapping * map) +{ + descriptor_table_free(map->table); + _mali_osk_mutex_rw_term(map->lock); + _mali_osk_free(map); +} + +_mali_osk_errcode_t mali_descriptor_mapping_allocate_mapping(mali_descriptor_mapping * map, void * target, int *odescriptor) +{ + _mali_osk_errcode_t err = _MALI_OSK_ERR_FAULT; + int new_descriptor; + + MALI_DEBUG_ASSERT_POINTER(map); + MALI_DEBUG_ASSERT_POINTER(odescriptor); + + _mali_osk_mutex_rw_wait(map->lock, _MALI_OSK_LOCKMODE_RW); + new_descriptor = _mali_osk_find_first_zero_bit(map->table->usage, map->current_nr_mappings); + if (new_descriptor == map->current_nr_mappings) { + /* no free descriptor, try to expand the table */ + mali_descriptor_table * new_table, * old_table; + if (map->current_nr_mappings >= map->max_nr_mappings_allowed) goto unlock_and_exit; + + map->current_nr_mappings += BITS_PER_LONG; + new_table = descriptor_table_alloc(map->current_nr_mappings); + if (NULL == new_table) goto unlock_and_exit; + + old_table = map->table; + _mali_osk_memcpy(new_table->usage, old_table->usage, (sizeof(unsigned long)*map->current_nr_mappings) / BITS_PER_LONG); + _mali_osk_memcpy(new_table->mappings, old_table->mappings, map->current_nr_mappings * sizeof(void*)); + map->table = new_table; + descriptor_table_free(old_table); + } + + /* we have found a valid descriptor, set the value and usage bit */ + _mali_osk_set_nonatomic_bit(new_descriptor, map->table->usage); + map->table->mappings[new_descriptor] = target; + *odescriptor = new_descriptor; + err = _MALI_OSK_ERR_OK; + +unlock_and_exit: + _mali_osk_mutex_rw_signal(map->lock, _MALI_OSK_LOCKMODE_RW); + MALI_ERROR(err); +} + +void mali_descriptor_mapping_call_for_each(mali_descriptor_mapping * map, void (*callback)(int, void*)) +{ + int i; + + MALI_DEBUG_ASSERT_POINTER(map); + MALI_DEBUG_ASSERT_POINTER(callback); + + _mali_osk_mutex_rw_wait(map->lock, _MALI_OSK_LOCKMODE_RO); + /* id 0 is skipped as it's an reserved ID not mapping to anything */ + for (i = 1; i < map->current_nr_mappings; ++i) { + if (_mali_osk_test_bit(i, map->table->usage)) { + callback(i, map->table->mappings[i]); + } + } + _mali_osk_mutex_rw_signal(map->lock, _MALI_OSK_LOCKMODE_RO); +} + +_mali_osk_errcode_t mali_descriptor_mapping_get(mali_descriptor_mapping * map, int descriptor, void** target) +{ + _mali_osk_errcode_t result = _MALI_OSK_ERR_FAULT; + MALI_DEBUG_ASSERT_POINTER(map); + _mali_osk_mutex_rw_wait(map->lock, _MALI_OSK_LOCKMODE_RO); + if ( (descriptor >= 0) && (descriptor < map->current_nr_mappings) && _mali_osk_test_bit(descriptor, map->table->usage) ) { + *target = map->table->mappings[descriptor]; + result = _MALI_OSK_ERR_OK; + } else *target = NULL; + _mali_osk_mutex_rw_signal(map->lock, _MALI_OSK_LOCKMODE_RO); + MALI_ERROR(result); +} + +_mali_osk_errcode_t mali_descriptor_mapping_set(mali_descriptor_mapping * map, int descriptor, void * target) +{ + _mali_osk_errcode_t result = _MALI_OSK_ERR_FAULT; + _mali_osk_mutex_rw_wait(map->lock, _MALI_OSK_LOCKMODE_RO); + if ( (descriptor >= 0) && (descriptor < map->current_nr_mappings) && _mali_osk_test_bit(descriptor, map->table->usage) ) { + map->table->mappings[descriptor] = target; + result = _MALI_OSK_ERR_OK; + } + _mali_osk_mutex_rw_signal(map->lock, _MALI_OSK_LOCKMODE_RO); + MALI_ERROR(result); +} + +void *mali_descriptor_mapping_free(mali_descriptor_mapping * map, int descriptor) +{ + void *old_value = NULL; + + _mali_osk_mutex_rw_wait(map->lock, _MALI_OSK_LOCKMODE_RW); + if ( (descriptor >= 0) && (descriptor < map->current_nr_mappings) && _mali_osk_test_bit(descriptor, map->table->usage) ) { + old_value = map->table->mappings[descriptor]; + map->table->mappings[descriptor] = NULL; + _mali_osk_clear_nonatomic_bit(descriptor, map->table->usage); + } + _mali_osk_mutex_rw_signal(map->lock, _MALI_OSK_LOCKMODE_RW); + + return old_value; +} + +static mali_descriptor_table * descriptor_table_alloc(int count) +{ + mali_descriptor_table * table; + + table = _mali_osk_calloc(1, sizeof(mali_descriptor_table) + ((sizeof(unsigned long) * count)/BITS_PER_LONG) + (sizeof(void*) * count)); + + if (NULL != table) { + table->usage = (u32*)((u8*)table + sizeof(mali_descriptor_table)); + table->mappings = (void**)((u8*)table + sizeof(mali_descriptor_table) + ((sizeof(unsigned long) * count)/BITS_PER_LONG)); + } + + return table; +} + +static void descriptor_table_free(mali_descriptor_table * table) +{ + _mali_osk_free(table); +} diff --git a/drivers/gpu/arm/mali/common/mali_kernel_descriptor_mapping.h b/drivers/gpu/arm/mali/common/mali_kernel_descriptor_mapping.h new file mode 100644 index 00000000000000..c7017e185432de --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_descriptor_mapping.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2010, 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_kernel_descriptor_mapping.h + */ + +#ifndef __MALI_KERNEL_DESCRIPTOR_MAPPING_H__ +#define __MALI_KERNEL_DESCRIPTOR_MAPPING_H__ + +#include "mali_osk.h" + +/** + * The actual descriptor mapping table, never directly accessed by clients + */ +typedef struct mali_descriptor_table { + u32 * usage; /**< Pointer to bitpattern indicating if a descriptor is valid/used or not */ + void** mappings; /**< Array of the pointers the descriptors map to */ +} mali_descriptor_table; + +/** + * The descriptor mapping object + * Provides a separate namespace where we can map an integer to a pointer + */ +typedef struct mali_descriptor_mapping { + _mali_osk_mutex_rw_t *lock; /**< Lock protecting access to the mapping object */ + int max_nr_mappings_allowed; /**< Max number of mappings to support in this namespace */ + int current_nr_mappings; /**< Current number of possible mappings */ + mali_descriptor_table * table; /**< Pointer to the current mapping table */ +} mali_descriptor_mapping; + +/** + * Create a descriptor mapping object + * Create a descriptor mapping capable of holding init_entries growable to max_entries + * @param init_entries Number of entries to preallocate memory for + * @param max_entries Number of entries to max support + * @return Pointer to a descriptor mapping object, NULL on failure + */ +mali_descriptor_mapping * mali_descriptor_mapping_create(int init_entries, int max_entries); + +/** + * Destroy a descriptor mapping object + * @param map The map to free + */ +void mali_descriptor_mapping_destroy(mali_descriptor_mapping * map); + +/** + * Allocate a new mapping entry (descriptor ID) + * Allocates a new entry in the map. + * @param map The map to allocate a new entry in + * @param target The value to map to + * @return The descriptor allocated, a negative value on error + */ +_mali_osk_errcode_t mali_descriptor_mapping_allocate_mapping(mali_descriptor_mapping * map, void * target, int *descriptor); + +/** + * Get the value mapped to by a descriptor ID + * @param map The map to lookup the descriptor id in + * @param descriptor The descriptor ID to lookup + * @param target Pointer to a pointer which will receive the stored value + * @return 0 on successful lookup, negative on error + */ +_mali_osk_errcode_t mali_descriptor_mapping_get(mali_descriptor_mapping * map, int descriptor, void** target); + +/** + * Set the value mapped to by a descriptor ID + * @param map The map to lookup the descriptor id in + * @param descriptor The descriptor ID to lookup + * @param target Pointer to replace the current value with + * @return 0 on successful lookup, negative on error + */ +_mali_osk_errcode_t mali_descriptor_mapping_set(mali_descriptor_mapping * map, int descriptor, void * target); + +/** + * Call the specified callback function for each descriptor in map. + * Entire function is mutex protected. + * @param map The map to do callbacks for + * @param callback A callback function which will be calle for each entry in map + */ +void mali_descriptor_mapping_call_for_each(mali_descriptor_mapping * map, void (*callback)(int, void*)); + +/** + * Free the descriptor ID + * For the descriptor to be reused it has to be freed + * @param map The map to free the descriptor from + * @param descriptor The descriptor ID to free + * + * @return old value of descriptor mapping + */ +void *mali_descriptor_mapping_free(mali_descriptor_mapping * map, int descriptor); + +#endif /* __MALI_KERNEL_DESCRIPTOR_MAPPING_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_kernel_utilization.c b/drivers/gpu/arm/mali/common/mali_kernel_utilization.c new file mode 100644 index 00000000000000..596015aad2b57c --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_utilization.c @@ -0,0 +1,439 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_utilization.h" +#include "mali_osk.h" +#include "mali_osk_mali.h" +#include "mali_kernel_common.h" +#include "mali_session.h" +#include "mali_scheduler.h" + +/* Thresholds for GP bound detection. */ +#define MALI_GP_BOUND_GP_UTILIZATION_THRESHOLD 240 +#define MALI_GP_BOUND_PP_UTILIZATION_THRESHOLD 250 + +/* Define how often to calculate and report GPU utilization, in milliseconds */ +static _mali_osk_spinlock_irq_t *time_data_lock; + +static u32 num_running_gp_cores; +static u32 num_running_pp_cores; + +static u64 work_start_time_gpu = 0; +static u64 work_start_time_gp = 0; +static u64 work_start_time_pp = 0; +static u64 accumulated_work_time_gpu = 0; +static u64 accumulated_work_time_gp = 0; +static u64 accumulated_work_time_pp = 0; + +static u64 period_start_time = 0; +static _mali_osk_timer_t *utilization_timer = NULL; +static mali_bool timer_running = MALI_FALSE; + +static u32 last_utilization_gpu = 0 ; +static u32 last_utilization_gp = 0 ; +static u32 last_utilization_pp = 0 ; + +static u32 mali_utilization_timeout = 1000; +void (*mali_utilization_callback)(struct mali_gpu_utilization_data *data) = NULL; +#if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY) +extern void mali_power_performance_policy_callback(struct mali_gpu_utilization_data *data); +#define NUMBER_OF_NANOSECONDS_PER_SECOND 1000000000ULL + +static u32 calculate_window_render_fps(u64 time_period) +{ + u32 max_window_number; + u64 tmp; + u64 max = time_period; + u32 leading_zeroes; + u32 shift_val; + u32 time_period_shift; + u32 max_window_number_shift; + u32 ret_val; + + max_window_number = mali_session_max_window_num(); + /* To avoid float division, extend the dividend to ns unit */ + tmp = (u64)max_window_number * NUMBER_OF_NANOSECONDS_PER_SECOND; + if (tmp > time_period) { + max = tmp; + } + + /* + * We may have 64-bit values, a dividend or a divisor or both + * To avoid dependencies to a 64-bit divider, we shift down the two values + * equally first. + */ + leading_zeroes = _mali_osk_clz((u32)(max >> 32)); + shift_val = 32 - leading_zeroes; + + time_period_shift = (u32)(time_period >> shift_val); + max_window_number_shift = (u32)(tmp >> shift_val); + + ret_val = max_window_number_shift / time_period_shift; + + return ret_val; +} +#endif /* defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY) */ + +static void calculate_gpu_utilization(void* arg) +{ + u64 time_now; + u64 time_period; + u32 leading_zeroes; + u32 shift_val; + u32 work_normalized_gpu; + u32 work_normalized_gp; + u32 work_normalized_pp; + u32 period_normalized; + u32 utilization_gpu; + u32 utilization_gp; + u32 utilization_pp; +#if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY) + u32 window_render_fps; +#endif + + _mali_osk_spinlock_irq_lock(time_data_lock); + + if (accumulated_work_time_gpu == 0 && work_start_time_gpu == 0) { + /* + * No work done for this period + * - No need to reschedule timer + * - Report zero usage + */ + timer_running = MALI_FALSE; + + last_utilization_gpu = 0; + last_utilization_gp = 0; + last_utilization_pp = 0; + + _mali_osk_spinlock_irq_unlock(time_data_lock); + + if (NULL != mali_utilization_callback) { + struct mali_gpu_utilization_data data = { 0, }; + mali_utilization_callback(&data); + } + + mali_scheduler_hint_disable(MALI_SCHEDULER_HINT_GP_BOUND); + + return; + } + + time_now = _mali_osk_time_get_ns(); + + time_period = time_now - period_start_time; + + /* If we are currently busy, update working period up to now */ + if (work_start_time_gpu != 0) { + accumulated_work_time_gpu += (time_now - work_start_time_gpu); + work_start_time_gpu = time_now; + + /* GP and/or PP will also be busy if the GPU is busy at this point */ + + if (work_start_time_gp != 0) { + accumulated_work_time_gp += (time_now - work_start_time_gp); + work_start_time_gp = time_now; + } + + if (work_start_time_pp != 0) { + accumulated_work_time_pp += (time_now - work_start_time_pp); + work_start_time_pp = time_now; + } + } + + /* + * We have two 64-bit values, a dividend and a divisor. + * To avoid dependencies to a 64-bit divider, we shift down the two values + * equally first. + * We shift the dividend up and possibly the divisor down, making the result X in 256. + */ + + /* Shift the 64-bit values down so they fit inside a 32-bit integer */ + leading_zeroes = _mali_osk_clz((u32)(time_period >> 32)); + shift_val = 32 - leading_zeroes; + work_normalized_gpu = (u32)(accumulated_work_time_gpu >> shift_val); + work_normalized_gp = (u32)(accumulated_work_time_gp >> shift_val); + work_normalized_pp = (u32)(accumulated_work_time_pp >> shift_val); + period_normalized = (u32)(time_period >> shift_val); + + /* + * Now, we should report the usage in parts of 256 + * this means we must shift up the dividend or down the divisor by 8 + * (we could do a combination, but we just use one for simplicity, + * but the end result should be good enough anyway) + */ + if (period_normalized > 0x00FFFFFF) { + /* The divisor is so big that it is safe to shift it down */ + period_normalized >>= 8; + } else { + /* + * The divisor is so small that we can shift up the dividend, without loosing any data. + * (dividend is always smaller than the divisor) + */ + work_normalized_gpu <<= 8; + work_normalized_gp <<= 8; + work_normalized_pp <<= 8; + } + + utilization_gpu = work_normalized_gpu / period_normalized; + utilization_gp = work_normalized_gp / period_normalized; + utilization_pp = work_normalized_pp / period_normalized; + +#if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY) + window_render_fps = calculate_window_render_fps(time_period); +#endif + + last_utilization_gpu = utilization_gpu; + last_utilization_gp = utilization_gp; + last_utilization_pp = utilization_pp; + + if ((MALI_GP_BOUND_GP_UTILIZATION_THRESHOLD < last_utilization_gp) && + (MALI_GP_BOUND_PP_UTILIZATION_THRESHOLD > last_utilization_pp)) { + mali_scheduler_hint_enable(MALI_SCHEDULER_HINT_GP_BOUND); + } else { + mali_scheduler_hint_disable(MALI_SCHEDULER_HINT_GP_BOUND); + } + + /* starting a new period */ + accumulated_work_time_gpu = 0; + accumulated_work_time_gp = 0; + accumulated_work_time_pp = 0; + period_start_time = time_now; + + _mali_osk_spinlock_irq_unlock(time_data_lock); + + _mali_osk_timer_add(utilization_timer, _mali_osk_time_mstoticks(mali_utilization_timeout)); + + if (NULL != mali_utilization_callback) { + struct mali_gpu_utilization_data data = { + utilization_gpu, utilization_gp, utilization_pp, +#if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY) + window_render_fps, window_render_fps +#endif + }; + mali_utilization_callback(&data); + } +} + +_mali_osk_errcode_t mali_utilization_init(void) +{ +#if USING_GPU_UTILIZATION + struct _mali_osk_device_data data; + if (_MALI_OSK_ERR_OK == _mali_osk_device_data_get(&data)) { + /* Use device specific settings (if defined) */ + if (0 != data.utilization_interval) { + mali_utilization_timeout = data.utilization_interval; + } + if (NULL != data.utilization_callback) { + mali_utilization_callback = data.utilization_callback; + MALI_DEBUG_PRINT(2, ("Mali GPU Utilization: Platform has it's own policy \n")); + MALI_DEBUG_PRINT(2, ("Mali GPU Utilization: Utilization handler installed with interval %u\n", mali_utilization_timeout)); + } + } +#endif + +#if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY) + if (mali_utilization_callback == NULL) { + MALI_DEBUG_PRINT(2, ("Mali GPU Utilization: MALI Power Performance Policy Algorithm \n")); + mali_utilization_callback = mali_power_performance_policy_callback; + } +#endif + + if (NULL == mali_utilization_callback) { + MALI_DEBUG_PRINT(2, ("Mali GPU Utilization: No utilization handler installed\n")); + } + + time_data_lock = _mali_osk_spinlock_irq_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_UTILIZATION); + + if (NULL == time_data_lock) { + return _MALI_OSK_ERR_FAULT; + } + + num_running_gp_cores = 0; + num_running_pp_cores = 0; + + utilization_timer = _mali_osk_timer_init(); + if (NULL == utilization_timer) { + _mali_osk_spinlock_irq_term(time_data_lock); + return _MALI_OSK_ERR_FAULT; + } + _mali_osk_timer_setcallback(utilization_timer, calculate_gpu_utilization, NULL); + + return _MALI_OSK_ERR_OK; +} + +void mali_utilization_suspend(void) +{ + _mali_osk_spinlock_irq_lock(time_data_lock); + + if (timer_running == MALI_TRUE) { + timer_running = MALI_FALSE; + _mali_osk_spinlock_irq_unlock(time_data_lock); + _mali_osk_timer_del(utilization_timer); + return; + } + + _mali_osk_spinlock_irq_unlock(time_data_lock); +} + +void mali_utilization_term(void) +{ + if (NULL != utilization_timer) { + _mali_osk_timer_del(utilization_timer); + timer_running = MALI_FALSE; + _mali_osk_timer_term(utilization_timer); + utilization_timer = NULL; + } + + _mali_osk_spinlock_irq_term(time_data_lock); +} + +void mali_utilization_gp_start(void) +{ + _mali_osk_spinlock_irq_lock(time_data_lock); + + ++num_running_gp_cores; + if (1 == num_running_gp_cores) { + u64 time_now = _mali_osk_time_get_ns(); + + /* First GP core started, consider GP busy from now and onwards */ + work_start_time_gp = time_now; + + if (0 == num_running_pp_cores) { + /* + * There are no PP cores running, so this is also the point + * at which we consider the GPU to be busy as well. + */ + work_start_time_gpu = time_now; + } + + /* Start a new period (and timer) if needed */ + if (timer_running != MALI_TRUE) { + timer_running = MALI_TRUE; + period_start_time = time_now; + + /* Clear session->number_of_window_jobs */ +#if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY) + mali_session_max_window_num(); +#endif + _mali_osk_spinlock_irq_unlock(time_data_lock); + + _mali_osk_timer_add(utilization_timer, _mali_osk_time_mstoticks(mali_utilization_timeout)); + } else { + _mali_osk_spinlock_irq_unlock(time_data_lock); + } + } else { + /* Nothing to do */ + _mali_osk_spinlock_irq_unlock(time_data_lock); + } +} + +void mali_utilization_pp_start(void) +{ + _mali_osk_spinlock_irq_lock(time_data_lock); + + ++num_running_pp_cores; + if (1 == num_running_pp_cores) { + u64 time_now = _mali_osk_time_get_ns(); + + /* First PP core started, consider PP busy from now and onwards */ + work_start_time_pp = time_now; + + if (0 == num_running_gp_cores) { + /* + * There are no GP cores running, so this is also the point + * at which we consider the GPU to be busy as well. + */ + work_start_time_gpu = time_now; + } + + /* Start a new period (and timer) if needed */ + if (timer_running != MALI_TRUE) { + timer_running = MALI_TRUE; + period_start_time = time_now; + + /* Clear session->number_of_window_jobs */ +#if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY) + mali_session_max_window_num(); +#endif + _mali_osk_spinlock_irq_unlock(time_data_lock); + + _mali_osk_timer_add(utilization_timer, _mali_osk_time_mstoticks(mali_utilization_timeout)); + } else { + _mali_osk_spinlock_irq_unlock(time_data_lock); + } + } else { + /* Nothing to do */ + _mali_osk_spinlock_irq_unlock(time_data_lock); + } +} + +void mali_utilization_gp_end(void) +{ + _mali_osk_spinlock_irq_lock(time_data_lock); + + --num_running_gp_cores; + if (0 == num_running_gp_cores) { + u64 time_now = _mali_osk_time_get_ns(); + + /* Last GP core ended, consider GP idle from now and onwards */ + accumulated_work_time_gp += (time_now - work_start_time_gp); + work_start_time_gp = 0; + + if (0 == num_running_pp_cores) { + /* + * There are no PP cores running, so this is also the point + * at which we consider the GPU to be idle as well. + */ + accumulated_work_time_gpu += (time_now - work_start_time_gpu); + work_start_time_gpu = 0; + } + } + + _mali_osk_spinlock_irq_unlock(time_data_lock); +} + +void mali_utilization_pp_end(void) +{ + _mali_osk_spinlock_irq_lock(time_data_lock); + + --num_running_pp_cores; + if (0 == num_running_pp_cores) { + u64 time_now = _mali_osk_time_get_ns(); + + /* Last PP core ended, consider PP idle from now and onwards */ + accumulated_work_time_pp += (time_now - work_start_time_pp); + work_start_time_pp = 0; + + if (0 == num_running_gp_cores) { + /* + * There are no GP cores running, so this is also the point + * at which we consider the GPU to be idle as well. + */ + accumulated_work_time_gpu += (time_now - work_start_time_gpu); + work_start_time_gpu = 0; + } + } + + _mali_osk_spinlock_irq_unlock(time_data_lock); +} + +u32 _mali_ukk_utilization_gp_pp(void) +{ + return last_utilization_gpu; +} + +u32 _mali_ukk_utilization_gp(void) +{ + return last_utilization_gp; +} + +u32 _mali_ukk_utilization_pp(void) +{ + return last_utilization_pp; +} diff --git a/drivers/gpu/arm/mali/common/mali_kernel_utilization.h b/drivers/gpu/arm/mali/common/mali_kernel_utilization.h new file mode 100644 index 00000000000000..3aa31483b4f4f3 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_utilization.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_UTILIZATION_H__ +#define __MALI_KERNEL_UTILIZATION_H__ + +#include +#include "mali_osk.h" + +extern void (*mali_utilization_callback)(struct mali_gpu_utilization_data *data); + +/** + * Initialize/start the Mali GPU utilization metrics reporting. + * + * @return _MALI_OSK_ERR_OK on success, otherwise failure. + */ +_mali_osk_errcode_t mali_utilization_init(void); + +/** + * Terminate the Mali GPU utilization metrics reporting + */ +void mali_utilization_term(void); + +/** + * Check if Mali utilization is enabled + */ +MALI_STATIC_INLINE mali_bool mali_utilization_enabled(void) +{ + return (NULL != mali_utilization_callback); +} + +/** + * Should be called when a job is about to execute a GP job + */ +void mali_utilization_gp_start(void); + +/** + * Should be called when a job has completed executing a GP job + */ +void mali_utilization_gp_end(void); + +/** + * Should be called when a job is about to execute a PP job + */ +void mali_utilization_pp_start(void); + +/** + * Should be called when a job has completed executing a PP job + */ +void mali_utilization_pp_end(void); + +/** + * Should be called to stop the utilization timer during system suspend + */ +void mali_utilization_suspend(void); + + +#endif /* __MALI_KERNEL_UTILIZATION_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_kernel_vsync.c b/drivers/gpu/arm/mali/common/mali_kernel_vsync.c new file mode 100644 index 00000000000000..a329cb0025341e --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_vsync.c @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2011-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_ukk.h" + +#if defined(CONFIG_MALI400_PROFILING) +#include "mali_osk_profiling.h" +#endif + +_mali_osk_errcode_t _mali_ukk_vsync_event_report(_mali_uk_vsync_event_report_s *args) +{ + _mali_uk_vsync_event event = (_mali_uk_vsync_event)args->event; + MALI_IGNORE(event); /* event is not used for release code, and that is OK */ + +#if defined(CONFIG_MALI400_PROFILING) + /* + * Manually generate user space events in kernel space. + * This saves user space from calling kernel space twice in this case. + * We just need to remember to add pid and tid manually. + */ + if ( event==_MALI_UK_VSYNC_EVENT_BEGIN_WAIT) { + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_SUSPEND | + MALI_PROFILING_EVENT_CHANNEL_SOFTWARE | + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_VSYNC, + _mali_osk_get_pid(), _mali_osk_get_tid(), 0, 0, 0); + } + + if (event==_MALI_UK_VSYNC_EVENT_END_WAIT) { + + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_RESUME | + MALI_PROFILING_EVENT_CHANNEL_SOFTWARE | + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_VSYNC, + _mali_osk_get_pid(), _mali_osk_get_tid(), 0, 0, 0); + } +#endif + + MALI_DEBUG_PRINT(4, ("Received VSYNC event: %d\n", event)); + MALI_SUCCESS; +} + diff --git a/drivers/gpu/arm/mali/common/mali_l2_cache.c b/drivers/gpu/arm/mali/common/mali_l2_cache.c new file mode 100644 index 00000000000000..2c6013e8382687 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_l2_cache.c @@ -0,0 +1,581 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_l2_cache.h" +#include "mali_hw_core.h" +#include "mali_scheduler.h" +#include "mali_pm_domain.h" + +/** + * Size of the Mali L2 cache registers in bytes + */ +#define MALI400_L2_CACHE_REGISTERS_SIZE 0x30 + +/** + * Mali L2 cache register numbers + * Used in the register read/write routines. + * See the hardware documentation for more information about each register + */ +typedef enum mali_l2_cache_register { + MALI400_L2_CACHE_REGISTER_SIZE = 0x0004, + MALI400_L2_CACHE_REGISTER_STATUS = 0x0008, + /*unused = 0x000C */ + MALI400_L2_CACHE_REGISTER_COMMAND = 0x0010, /**< Misc cache commands, e.g. clear */ + MALI400_L2_CACHE_REGISTER_CLEAR_PAGE = 0x0014, + MALI400_L2_CACHE_REGISTER_MAX_READS = 0x0018, /**< Limit of outstanding read requests */ + MALI400_L2_CACHE_REGISTER_ENABLE = 0x001C, /**< Enable misc cache features */ + MALI400_L2_CACHE_REGISTER_PERFCNT_SRC0 = 0x0020, + MALI400_L2_CACHE_REGISTER_PERFCNT_VAL0 = 0x0024, + MALI400_L2_CACHE_REGISTER_PERFCNT_SRC1 = 0x0028, + MALI400_L2_CACHE_REGISTER_PERFCNT_VAL1 = 0x002C, +} mali_l2_cache_register; + +/** + * Mali L2 cache commands + * These are the commands that can be sent to the Mali L2 cache unit + */ +typedef enum mali_l2_cache_command { + MALI400_L2_CACHE_COMMAND_CLEAR_ALL = 0x01, /**< Clear the entire cache */ + /* Read HW TRM carefully before adding/using other commands than the clear above */ +} mali_l2_cache_command; + +/** + * Mali L2 cache commands + * These are the commands that can be sent to the Mali L2 cache unit + */ +typedef enum mali_l2_cache_enable { + MALI400_L2_CACHE_ENABLE_DEFAULT = 0x0, /**< Default state of enable register */ + MALI400_L2_CACHE_ENABLE_ACCESS = 0x01, /**< Permit cacheable accesses */ + MALI400_L2_CACHE_ENABLE_READ_ALLOCATE = 0x02, /**< Permit cache read allocate */ +} mali_l2_cache_enable; + +/** + * Mali L2 cache status bits + */ +typedef enum mali_l2_cache_status { + MALI400_L2_CACHE_STATUS_COMMAND_BUSY = 0x01, /**< Command handler of L2 cache is busy */ + MALI400_L2_CACHE_STATUS_DATA_BUSY = 0x02, /**< L2 cache is busy handling data requests */ +} mali_l2_cache_status; + +#define MALI400_L2_MAX_READS_DEFAULT 0x1C + +static struct mali_l2_cache_core *mali_global_l2_cache_cores[MALI_MAX_NUMBER_OF_L2_CACHE_CORES] = { NULL, }; +static u32 mali_global_num_l2_cache_cores = 0; + +int mali_l2_max_reads = MALI400_L2_MAX_READS_DEFAULT; + + +/* Local helper functions */ +static _mali_osk_errcode_t mali_l2_cache_send_command(struct mali_l2_cache_core *cache, u32 reg, u32 val); + + +static void mali_l2_cache_counter_lock(struct mali_l2_cache_core *cache) +{ +#ifdef MALI_UPPER_HALF_SCHEDULING + _mali_osk_spinlock_irq_lock(cache->counter_lock); +#else + _mali_osk_spinlock_lock(cache->counter_lock); +#endif +} + +static void mali_l2_cache_counter_unlock(struct mali_l2_cache_core *cache) +{ +#ifdef MALI_UPPER_HALF_SCHEDULING + _mali_osk_spinlock_irq_unlock(cache->counter_lock); +#else + _mali_osk_spinlock_unlock(cache->counter_lock); +#endif +} + +static void mali_l2_cache_command_lock(struct mali_l2_cache_core *cache) +{ +#ifdef MALI_UPPER_HALF_SCHEDULING + _mali_osk_spinlock_irq_lock(cache->command_lock); +#else + _mali_osk_spinlock_lock(cache->command_lock); +#endif +} + +static void mali_l2_cache_command_unlock(struct mali_l2_cache_core *cache) +{ +#ifdef MALI_UPPER_HALF_SCHEDULING + _mali_osk_spinlock_irq_unlock(cache->command_lock); +#else + _mali_osk_spinlock_unlock(cache->command_lock); +#endif +} + +struct mali_l2_cache_core *mali_l2_cache_create(_mali_osk_resource_t *resource) +{ + struct mali_l2_cache_core *cache = NULL; + + MALI_DEBUG_PRINT(4, ("Mali L2 cache: Creating Mali L2 cache: %s\n", resource->description)); + + if (mali_global_num_l2_cache_cores >= MALI_MAX_NUMBER_OF_L2_CACHE_CORES) { + MALI_PRINT_ERROR(("Mali L2 cache: Too many L2 cache core objects created\n")); + return NULL; + } + + cache = _mali_osk_malloc(sizeof(struct mali_l2_cache_core)); + if (NULL != cache) { + cache->core_id = mali_global_num_l2_cache_cores; + cache->counter_src0 = MALI_HW_CORE_NO_COUNTER; + cache->counter_src1 = MALI_HW_CORE_NO_COUNTER; + cache->pm_domain = NULL; + cache->mali_l2_status = MALI_L2_NORMAL; + if (_MALI_OSK_ERR_OK == mali_hw_core_create(&cache->hw_core, resource, MALI400_L2_CACHE_REGISTERS_SIZE)) { + MALI_DEBUG_CODE(u32 cache_size = mali_hw_core_register_read(&cache->hw_core, MALI400_L2_CACHE_REGISTER_SIZE)); + MALI_DEBUG_PRINT(2, ("Mali L2 cache: Created %s: % 3uK, %u-way, % 2ubyte cache line, % 3ubit external bus\n", + resource->description, + 1 << (((cache_size >> 16) & 0xff) - 10), + 1 << ((cache_size >> 8) & 0xff), + 1 << (cache_size & 0xff), + 1 << ((cache_size >> 24) & 0xff))); + +#ifdef MALI_UPPER_HALF_SCHEDULING + cache->command_lock = _mali_osk_spinlock_irq_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_L2_COMMAND); +#else + cache->command_lock = _mali_osk_spinlock_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_L2_COMMAND); +#endif + if (NULL != cache->command_lock) { +#ifdef MALI_UPPER_HALF_SCHEDULING + cache->counter_lock = _mali_osk_spinlock_irq_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_L2_COMMAND); +#else + cache->counter_lock = _mali_osk_spinlock_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_L2_COMMAND); +#endif + if (NULL != cache->counter_lock) { + mali_l2_cache_reset(cache); + + cache->last_invalidated_id = 0; + + mali_global_l2_cache_cores[mali_global_num_l2_cache_cores] = cache; + mali_global_num_l2_cache_cores++; + + return cache; + } else { + MALI_PRINT_ERROR(("Mali L2 cache: Failed to create counter lock for L2 cache core %s\n", cache->hw_core.description)); + } +#ifdef MALI_UPPER_HALF_SCHEDULING + _mali_osk_spinlock_irq_term(cache->command_lock); +#else + _mali_osk_spinlock_term(cache->command_lock); +#endif + } else { + MALI_PRINT_ERROR(("Mali L2 cache: Failed to create command lock for L2 cache core %s\n", cache->hw_core.description)); + } + + mali_hw_core_delete(&cache->hw_core); + } + + _mali_osk_free(cache); + } else { + MALI_PRINT_ERROR(("Mali L2 cache: Failed to allocate memory for L2 cache core\n")); + } + + return NULL; +} + +void mali_l2_cache_delete(struct mali_l2_cache_core *cache) +{ + u32 i; + + /* reset to defaults */ + mali_hw_core_register_write(&cache->hw_core, MALI400_L2_CACHE_REGISTER_MAX_READS, (u32)MALI400_L2_MAX_READS_DEFAULT); + mali_hw_core_register_write(&cache->hw_core, MALI400_L2_CACHE_REGISTER_ENABLE, (u32)MALI400_L2_CACHE_ENABLE_DEFAULT); + +#ifdef MALI_UPPER_HALF_SCHEDULING + _mali_osk_spinlock_irq_term(cache->counter_lock); + _mali_osk_spinlock_irq_term(cache->command_lock); +#else + _mali_osk_spinlock_term(cache->command_lock); + _mali_osk_spinlock_term(cache->counter_lock); +#endif + + mali_hw_core_delete(&cache->hw_core); + + for (i = 0; i < mali_global_num_l2_cache_cores; i++) { + if (mali_global_l2_cache_cores[i] == cache) { + mali_global_l2_cache_cores[i] = NULL; + mali_global_num_l2_cache_cores--; + + if (i != mali_global_num_l2_cache_cores) { + /* We removed a l2 cache from the middle of the array -- move the last + * l2 cache to the current position to close the gap */ + mali_global_l2_cache_cores[i] = mali_global_l2_cache_cores[mali_global_num_l2_cache_cores]; + mali_global_l2_cache_cores[mali_global_num_l2_cache_cores] = NULL; + } + + break; + } + } + + _mali_osk_free(cache); +} + +u32 mali_l2_cache_get_id(struct mali_l2_cache_core *cache) +{ + return cache->core_id; +} + +static void mali_l2_cache_core_set_counter_internal(struct mali_l2_cache_core *cache, u32 source_id, u32 counter) +{ + u32 value = 0; /* disabled src */ + u32 reg_offset = 0; + mali_bool core_is_on; + + MALI_DEBUG_ASSERT_POINTER(cache); + + core_is_on = mali_l2_cache_lock_power_state(cache); + + mali_l2_cache_counter_lock(cache); + + switch (source_id) { + case 0: + cache->counter_src0 = counter; + reg_offset = MALI400_L2_CACHE_REGISTER_PERFCNT_SRC0; + break; + + case 1: + cache->counter_src1 = counter; + reg_offset = MALI400_L2_CACHE_REGISTER_PERFCNT_SRC1; + break; + + default: + MALI_DEBUG_ASSERT(0); + break; + } + + if (MALI_L2_PAUSE == cache->mali_l2_status) { + mali_l2_cache_counter_unlock(cache); + mali_l2_cache_unlock_power_state(cache); + return; + } + + if (MALI_HW_CORE_NO_COUNTER != counter) { + value = counter; + } + + if (MALI_TRUE == core_is_on) { + mali_hw_core_register_write(&cache->hw_core, reg_offset, value); + } + + mali_l2_cache_counter_unlock(cache); + mali_l2_cache_unlock_power_state(cache); +} + +void mali_l2_cache_core_set_counter_src0(struct mali_l2_cache_core *cache, u32 counter) +{ + mali_l2_cache_core_set_counter_internal(cache, 0, counter); +} + +void mali_l2_cache_core_set_counter_src1(struct mali_l2_cache_core *cache, u32 counter) +{ + mali_l2_cache_core_set_counter_internal(cache, 1, counter); +} + +u32 mali_l2_cache_core_get_counter_src0(struct mali_l2_cache_core *cache) +{ + return cache->counter_src0; +} + +u32 mali_l2_cache_core_get_counter_src1(struct mali_l2_cache_core *cache) +{ + return cache->counter_src1; +} + +void mali_l2_cache_core_get_counter_values(struct mali_l2_cache_core *cache, u32 *src0, u32 *value0, u32 *src1, u32 *value1) +{ + MALI_DEBUG_ASSERT(NULL != src0); + MALI_DEBUG_ASSERT(NULL != value0); + MALI_DEBUG_ASSERT(NULL != src1); + MALI_DEBUG_ASSERT(NULL != value1); + + /* Caller must hold the PM lock and know that we are powered on */ + + mali_l2_cache_counter_lock(cache); + + if (MALI_L2_PAUSE == cache->mali_l2_status) { + mali_l2_cache_counter_unlock(cache); + + return; + } + + *src0 = cache->counter_src0; + *src1 = cache->counter_src1; + + if (cache->counter_src0 != MALI_HW_CORE_NO_COUNTER) { + *value0 = mali_hw_core_register_read(&cache->hw_core, MALI400_L2_CACHE_REGISTER_PERFCNT_VAL0); + } + + if (cache->counter_src1 != MALI_HW_CORE_NO_COUNTER) { + *value1 = mali_hw_core_register_read(&cache->hw_core, MALI400_L2_CACHE_REGISTER_PERFCNT_VAL1); + } + + mali_l2_cache_counter_unlock(cache); +} + +static void mali_l2_cache_reset_counters_all(void) +{ + int i; + u32 value; + struct mali_l2_cache_core *cache; + u32 num_cores = mali_l2_cache_core_get_glob_num_l2_cores(); + + for (i = 0; i < num_cores; i++) { + cache = mali_l2_cache_core_get_glob_l2_core(i); + if (MALI_TRUE == mali_l2_cache_lock_power_state(cache)) { + mali_l2_cache_counter_lock(cache); + + if (MALI_L2_PAUSE == cache->mali_l2_status) { + mali_l2_cache_counter_unlock(cache); + mali_l2_cache_unlock_power_state(cache); + return; + } + + /* Reset performance counters */ + if (MALI_HW_CORE_NO_COUNTER == cache->counter_src0) { + value = 0; + } else { + value = cache->counter_src0; + } + mali_hw_core_register_write(&cache->hw_core, + MALI400_L2_CACHE_REGISTER_PERFCNT_SRC0, value); + + if (MALI_HW_CORE_NO_COUNTER == cache->counter_src1) { + value = 0; + } else { + value = cache->counter_src1; + } + mali_hw_core_register_write(&cache->hw_core, + MALI400_L2_CACHE_REGISTER_PERFCNT_SRC1, value); + + mali_l2_cache_counter_unlock(cache); + } + + mali_l2_cache_unlock_power_state(cache); + } +} + + +struct mali_l2_cache_core *mali_l2_cache_core_get_glob_l2_core(u32 index) +{ + if (mali_global_num_l2_cache_cores > index) { + return mali_global_l2_cache_cores[index]; + } + + return NULL; +} + +u32 mali_l2_cache_core_get_glob_num_l2_cores(void) +{ + return mali_global_num_l2_cache_cores; +} + +void mali_l2_cache_reset(struct mali_l2_cache_core *cache) +{ + /* Invalidate cache (just to keep it in a known state at startup) */ + mali_l2_cache_send_command(cache, MALI400_L2_CACHE_REGISTER_COMMAND, MALI400_L2_CACHE_COMMAND_CLEAR_ALL); + + mali_l2_cache_counter_lock(cache); + + if (MALI_L2_PAUSE == cache->mali_l2_status) { + mali_l2_cache_counter_unlock(cache); + + return; + } + + /* Enable cache */ + mali_hw_core_register_write(&cache->hw_core, MALI400_L2_CACHE_REGISTER_ENABLE, (u32)MALI400_L2_CACHE_ENABLE_ACCESS | (u32)MALI400_L2_CACHE_ENABLE_READ_ALLOCATE); + mali_hw_core_register_write(&cache->hw_core, MALI400_L2_CACHE_REGISTER_MAX_READS, (u32)mali_l2_max_reads); + + /* Restart any performance counters (if enabled) */ + if (cache->counter_src0 != MALI_HW_CORE_NO_COUNTER) { + mali_hw_core_register_write(&cache->hw_core, MALI400_L2_CACHE_REGISTER_PERFCNT_SRC0, cache->counter_src0); + } + + if (cache->counter_src1 != MALI_HW_CORE_NO_COUNTER) { + mali_hw_core_register_write(&cache->hw_core, MALI400_L2_CACHE_REGISTER_PERFCNT_SRC1, cache->counter_src1); + } + + mali_l2_cache_counter_unlock(cache); +} + +void mali_l2_cache_reset_all(void) +{ + int i; + u32 num_cores = mali_l2_cache_core_get_glob_num_l2_cores(); + + for (i = 0; i < num_cores; i++) { + mali_l2_cache_reset(mali_l2_cache_core_get_glob_l2_core(i)); + } +} + +void mali_l2_cache_invalidate(struct mali_l2_cache_core *cache) +{ + MALI_DEBUG_ASSERT_POINTER(cache); + + if (NULL != cache) { + cache->last_invalidated_id = mali_scheduler_get_new_cache_order(); + mali_l2_cache_send_command(cache, MALI400_L2_CACHE_REGISTER_COMMAND, MALI400_L2_CACHE_COMMAND_CLEAR_ALL); + } +} + +mali_bool mali_l2_cache_invalidate_conditional(struct mali_l2_cache_core *cache, u32 id) +{ + MALI_DEBUG_ASSERT_POINTER(cache); + + if (NULL != cache) { + /* If the last cache invalidation was done by a job with a higher id we + * don't have to flush. Since user space will store jobs w/ their + * corresponding memory in sequence (first job #0, then job #1, ...), + * we don't have to flush for job n-1 if job n has already invalidated + * the cache since we know for sure that job n-1's memory was already + * written when job n was started. */ + if (((s32)id) <= ((s32)cache->last_invalidated_id)) { + return MALI_FALSE; + } else { + cache->last_invalidated_id = mali_scheduler_get_new_cache_order(); + } + + mali_l2_cache_send_command(cache, MALI400_L2_CACHE_REGISTER_COMMAND, MALI400_L2_CACHE_COMMAND_CLEAR_ALL); + } + return MALI_TRUE; +} + +void mali_l2_cache_invalidate_all(void) +{ + u32 i; + for (i = 0; i < mali_global_num_l2_cache_cores; i++) { + /*additional check*/ + if (MALI_TRUE == mali_l2_cache_lock_power_state(mali_global_l2_cache_cores[i])) { + _mali_osk_errcode_t ret; + mali_global_l2_cache_cores[i]->last_invalidated_id = mali_scheduler_get_new_cache_order(); + ret = mali_l2_cache_send_command(mali_global_l2_cache_cores[i], MALI400_L2_CACHE_REGISTER_COMMAND, MALI400_L2_CACHE_COMMAND_CLEAR_ALL); + if (_MALI_OSK_ERR_OK != ret) { + MALI_PRINT_ERROR(("Failed to invalidate cache\n")); + } + } + mali_l2_cache_unlock_power_state(mali_global_l2_cache_cores[i]); + } +} + +void mali_l2_cache_invalidate_all_pages(u32 *pages, u32 num_pages) +{ + u32 i; + for (i = 0; i < mali_global_num_l2_cache_cores; i++) { + /*additional check*/ + if (MALI_TRUE == mali_l2_cache_lock_power_state(mali_global_l2_cache_cores[i])) { + u32 j; + for (j = 0; j < num_pages; j++) { + _mali_osk_errcode_t ret; + ret = mali_l2_cache_send_command(mali_global_l2_cache_cores[i], MALI400_L2_CACHE_REGISTER_CLEAR_PAGE, pages[j]); + if (_MALI_OSK_ERR_OK != ret) { + MALI_PRINT_ERROR(("Failed to invalidate page cache\n")); + } + } + } + mali_l2_cache_unlock_power_state(mali_global_l2_cache_cores[i]); + } +} + +mali_bool mali_l2_cache_lock_power_state(struct mali_l2_cache_core *cache) +{ + return mali_pm_domain_lock_state(cache->pm_domain); +} + +void mali_l2_cache_unlock_power_state(struct mali_l2_cache_core *cache) +{ + return mali_pm_domain_unlock_state(cache->pm_domain); +} + +/* -------- local helper functions below -------- */ + + +static _mali_osk_errcode_t mali_l2_cache_send_command(struct mali_l2_cache_core *cache, u32 reg, u32 val) +{ + int i = 0; + const int loop_count = 100000; + + /* + * Grab lock in order to send commands to the L2 cache in a serialized fashion. + * The L2 cache will ignore commands if it is busy. + */ + mali_l2_cache_command_lock(cache); + + if (MALI_L2_PAUSE == cache->mali_l2_status) { + mali_l2_cache_command_unlock(cache); + MALI_DEBUG_PRINT(1, ( "Mali L2 cache: aborting wait for L2 come back\n")); + + MALI_ERROR( _MALI_OSK_ERR_BUSY ); + } + + /* First, wait for L2 cache command handler to go idle */ + + for (i = 0; i < loop_count; i++) { + if (!(mali_hw_core_register_read(&cache->hw_core, MALI400_L2_CACHE_REGISTER_STATUS) & (u32)MALI400_L2_CACHE_STATUS_COMMAND_BUSY)) { + break; + } + } + + if (i == loop_count) { + mali_l2_cache_command_unlock(cache); + MALI_DEBUG_PRINT(1, ( "Mali L2 cache: aborting wait for command interface to go idle\n")); + MALI_ERROR( _MALI_OSK_ERR_FAULT ); + } + + /* then issue the command */ + mali_hw_core_register_write(&cache->hw_core, reg, val); + + mali_l2_cache_command_unlock(cache); + + MALI_SUCCESS; +} + +void mali_l2_cache_pause_all(mali_bool pause) +{ + int i; + struct mali_l2_cache_core * cache; + u32 num_cores = mali_l2_cache_core_get_glob_num_l2_cores(); + mali_l2_power_status status = MALI_L2_NORMAL; + + if (pause) { + status = MALI_L2_PAUSE; + } + + for (i = 0; i < num_cores; i++) { + cache = mali_l2_cache_core_get_glob_l2_core(i); + if (NULL != cache) { + cache->mali_l2_status = status; + + /* Take and release the counter and command locks to + * ensure there are no active threads that didn't get + * the status flag update. + * + * The locks will also ensure the necessary memory + * barriers are done on SMP systems. + */ + mali_l2_cache_counter_lock(cache); + mali_l2_cache_counter_unlock(cache); + + mali_l2_cache_command_lock(cache); + mali_l2_cache_command_unlock(cache); + } + } + + /* Resume from pause: do the cache invalidation here to prevent any + * loss of cache operation during the pause period to make sure the SW + * status is consistent with L2 cache status. + */ + if(!pause) { + mali_l2_cache_invalidate_all(); + mali_l2_cache_reset_counters_all(); + } +} diff --git a/drivers/gpu/arm/mali/common/mali_l2_cache.h b/drivers/gpu/arm/mali/common/mali_l2_cache.h new file mode 100644 index 00000000000000..afa7b825afe189 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_l2_cache.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_L2_CACHE_H__ +#define __MALI_KERNEL_L2_CACHE_H__ + +#include "mali_osk.h" +#include "mali_hw_core.h" + +#define MALI_MAX_NUMBER_OF_L2_CACHE_CORES 3 +/* Maximum 1 GP and 4 PP for an L2 cache core (Mali-400 Quad-core) */ +#define MALI_MAX_NUMBER_OF_GROUPS_PER_L2_CACHE 5 + +struct mali_group; +struct mali_pm_domain; + +/* Flags describing state of the L2 */ +typedef enum mali_l2_power_status { + MALI_L2_NORMAL, /**< L2 is in normal state and operational */ + MALI_L2_PAUSE, /**< L2 may not be accessed and may be powered off */ +} mali_l2_power_status; + +/** + * Definition of the L2 cache core struct + * Used to track a L2 cache unit in the system. + * Contains information about the mapping of the registers + */ +struct mali_l2_cache_core { + struct mali_hw_core hw_core; /**< Common for all HW cores */ + u32 core_id; /**< Unique core ID */ +#ifdef MALI_UPPER_HALF_SCHEDULING + _mali_osk_spinlock_irq_t *command_lock; /**< Serialize all L2 cache commands */ + _mali_osk_spinlock_irq_t *counter_lock; /**< Synchronize L2 cache counter access */ +#else + _mali_osk_spinlock_t *command_lock; + _mali_osk_spinlock_t *counter_lock; +#endif + u32 counter_src0; /**< Performance counter 0, MALI_HW_CORE_NO_COUNTER for disabled */ + u32 counter_src1; /**< Performance counter 1, MALI_HW_CORE_NO_COUNTER for disabled */ + u32 last_invalidated_id; + struct mali_pm_domain *pm_domain; + mali_l2_power_status mali_l2_status; /**< Indicate whether the L2 is paused or not */ +}; + +_mali_osk_errcode_t mali_l2_cache_initialize(void); +void mali_l2_cache_terminate(void); +/** + * L2 pause is just a status that the L2 can't be accessed temporarily. +*/ +void mali_l2_cache_pause_all(mali_bool pause); +struct mali_l2_cache_core *mali_l2_cache_create(_mali_osk_resource_t * resource); +void mali_l2_cache_delete(struct mali_l2_cache_core *cache); + +MALI_STATIC_INLINE void mali_l2_cache_set_pm_domain(struct mali_l2_cache_core *cache, struct mali_pm_domain *domain) +{ + cache->pm_domain = domain; +} + +u32 mali_l2_cache_get_id(struct mali_l2_cache_core *cache); + +void mali_l2_cache_core_set_counter_src0(struct mali_l2_cache_core *cache, u32 counter); +void mali_l2_cache_core_set_counter_src1(struct mali_l2_cache_core *cache, u32 counter); +u32 mali_l2_cache_core_get_counter_src0(struct mali_l2_cache_core *cache); +u32 mali_l2_cache_core_get_counter_src1(struct mali_l2_cache_core *cache); +void mali_l2_cache_core_get_counter_values(struct mali_l2_cache_core *cache, u32 *src0, u32 *value0, u32 *src1, u32 *value1); +struct mali_l2_cache_core *mali_l2_cache_core_get_glob_l2_core(u32 index); +u32 mali_l2_cache_core_get_glob_num_l2_cores(void); + +void mali_l2_cache_reset(struct mali_l2_cache_core *cache); +void mali_l2_cache_reset_all(void); + +struct mali_group *mali_l2_cache_get_group(struct mali_l2_cache_core *cache, u32 index); + +void mali_l2_cache_invalidate(struct mali_l2_cache_core *cache); +mali_bool mali_l2_cache_invalidate_conditional(struct mali_l2_cache_core *cache, u32 id); +void mali_l2_cache_invalidate_all(void); +void mali_l2_cache_invalidate_all_pages(u32 *pages, u32 num_pages); + +mali_bool mali_l2_cache_lock_power_state(struct mali_l2_cache_core *cache); +void mali_l2_cache_unlock_power_state(struct mali_l2_cache_core *cache); + +#endif /* __MALI_KERNEL_L2_CACHE_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_mem_validation.c b/drivers/gpu/arm/mali/common/mali_mem_validation.c new file mode 100644 index 00000000000000..c72eb74864fb76 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_mem_validation.c @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2011-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_mem_validation.h" +#include "mali_osk.h" +#include "mali_kernel_common.h" + +#define MALI_INVALID_MEM_ADDR 0xFFFFFFFF + +typedef struct { + u32 phys_base; /**< Mali physical base of the memory, page aligned */ + u32 size; /**< size in bytes of the memory, multiple of page size */ +} _mali_mem_validation_t; + +static _mali_mem_validation_t mali_mem_validator = { MALI_INVALID_MEM_ADDR, MALI_INVALID_MEM_ADDR }; + +_mali_osk_errcode_t mali_mem_validation_add_range(u32 start, u32 size) +{ + /* Check that no other MEM_VALIDATION resources exist */ + if (MALI_INVALID_MEM_ADDR != mali_mem_validator.phys_base) { + MALI_PRINT_ERROR(("Failed to add frame buffer memory; another range is already specified\n")); + return _MALI_OSK_ERR_FAULT; + } + + /* Check restrictions on page alignment */ + if ((0 != (start & (~_MALI_OSK_CPU_PAGE_MASK))) || + (0 != (size & (~_MALI_OSK_CPU_PAGE_MASK)))) { + MALI_PRINT_ERROR(("Failed to add frame buffer memory; incorrect alignment\n")); + return _MALI_OSK_ERR_FAULT; + } + + mali_mem_validator.phys_base = start; + mali_mem_validator.size = size; + MALI_DEBUG_PRINT(2, ("Memory Validator installed for Mali physical address base=0x%08X, size=0x%08X\n", + mali_mem_validator.phys_base, mali_mem_validator.size)); + + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t mali_mem_validation_check(u32 phys_addr, u32 size) +{ + if (phys_addr < (phys_addr + size)) { /* Don't allow overflow (or zero size) */ + if ((0 == ( phys_addr & (~_MALI_OSK_CPU_PAGE_MASK))) && + (0 == ( size & (~_MALI_OSK_CPU_PAGE_MASK)))) { + if ((phys_addr >= mali_mem_validator.phys_base) && + ((phys_addr + (size - 1)) >= mali_mem_validator.phys_base) && + (phys_addr <= (mali_mem_validator.phys_base + (mali_mem_validator.size - 1))) && + ((phys_addr + (size - 1)) <= (mali_mem_validator.phys_base + (mali_mem_validator.size - 1))) ) { + MALI_DEBUG_PRINT(3, ("Accepted range 0x%08X + size 0x%08X (= 0x%08X)\n", phys_addr, size, (phys_addr + size - 1))); + return _MALI_OSK_ERR_OK; + } + } + } + + MALI_PRINT_ERROR(("MALI PHYSICAL RANGE VALIDATION ERROR: The range supplied was: phys_base=0x%08X, size=0x%08X\n", phys_addr, size)); + + return _MALI_OSK_ERR_FAULT; +} diff --git a/drivers/gpu/arm/mali/common/mali_mem_validation.h b/drivers/gpu/arm/mali/common/mali_mem_validation.h new file mode 100644 index 00000000000000..a9ab6d4ca7118f --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_mem_validation.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2011-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_MEM_VALIDATION_H__ +#define __MALI_MEM_VALIDATION_H__ + +#include "mali_osk.h" + +_mali_osk_errcode_t mali_mem_validation_add_range(u32 start, u32 size); +_mali_osk_errcode_t mali_mem_validation_check(u32 phys_addr, u32 size); + +#endif /* __MALI_MEM_VALIDATION_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_mmu.c b/drivers/gpu/arm/mali/common/mali_mmu.c new file mode 100644 index 00000000000000..896325ce139d01 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_mmu.c @@ -0,0 +1,430 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_osk_list.h" +#include "mali_ukk.h" + +#include "mali_mmu.h" +#include "mali_hw_core.h" +#include "mali_group.h" +#include "mali_mmu_page_directory.h" + +/** + * Size of the MMU registers in bytes + */ +#define MALI_MMU_REGISTERS_SIZE 0x24 + +/** + * MMU commands + * These are the commands that can be sent + * to the MMU unit. + */ +typedef enum mali_mmu_command { + MALI_MMU_COMMAND_ENABLE_PAGING = 0x00, /**< Enable paging (memory translation) */ + MALI_MMU_COMMAND_DISABLE_PAGING = 0x01, /**< Disable paging (memory translation) */ + MALI_MMU_COMMAND_ENABLE_STALL = 0x02, /**< Enable stall on page fault */ + MALI_MMU_COMMAND_DISABLE_STALL = 0x03, /**< Disable stall on page fault */ + MALI_MMU_COMMAND_ZAP_CACHE = 0x04, /**< Zap the entire page table cache */ + MALI_MMU_COMMAND_PAGE_FAULT_DONE = 0x05, /**< Page fault processed */ + MALI_MMU_COMMAND_HARD_RESET = 0x06 /**< Reset the MMU back to power-on settings */ +} mali_mmu_command; + +static void mali_mmu_probe_trigger(void *data); +static _mali_osk_errcode_t mali_mmu_probe_ack(void *data); + +MALI_STATIC_INLINE _mali_osk_errcode_t mali_mmu_raw_reset(struct mali_mmu_core *mmu); + +/* page fault queue flush helper pages + * note that the mapping pointers are currently unused outside of the initialization functions */ +static u32 mali_page_fault_flush_page_directory = MALI_INVALID_PAGE; +static mali_io_address mali_page_fault_flush_page_directory_mapping = NULL; +static u32 mali_page_fault_flush_page_table = MALI_INVALID_PAGE; +static mali_io_address mali_page_fault_flush_page_table_mapping = NULL; +static u32 mali_page_fault_flush_data_page = MALI_INVALID_PAGE; +static mali_io_address mali_page_fault_flush_data_page_mapping = NULL; + +/* an empty page directory (no address valid) which is active on any MMU not currently marked as in use */ +static u32 mali_empty_page_directory_phys = MALI_INVALID_PAGE; +static mali_io_address mali_empty_page_directory_virt = NULL; + + +_mali_osk_errcode_t mali_mmu_initialize(void) +{ + /* allocate the helper pages */ + mali_empty_page_directory_phys = mali_allocate_empty_page(&mali_empty_page_directory_virt); + if(0 == mali_empty_page_directory_phys) { + MALI_DEBUG_PRINT_ERROR(("Mali MMU: Could not allocate empty page directory.\n")); + mali_empty_page_directory_phys = MALI_INVALID_PAGE; + return _MALI_OSK_ERR_NOMEM; + } + + if (_MALI_OSK_ERR_OK != mali_create_fault_flush_pages(&mali_page_fault_flush_page_directory, + &mali_page_fault_flush_page_directory_mapping, + &mali_page_fault_flush_page_table, + &mali_page_fault_flush_page_table_mapping, + &mali_page_fault_flush_data_page, + &mali_page_fault_flush_data_page_mapping)) { + MALI_DEBUG_PRINT_ERROR(("Mali MMU: Could not allocate fault flush pages\n")); + mali_free_empty_page(mali_empty_page_directory_phys, mali_empty_page_directory_virt); + mali_empty_page_directory_phys = MALI_INVALID_PAGE; + mali_empty_page_directory_virt = NULL; + return _MALI_OSK_ERR_NOMEM; + } + + return _MALI_OSK_ERR_OK; +} + +void mali_mmu_terminate(void) +{ + MALI_DEBUG_PRINT(3, ("Mali MMU: terminating\n")); + + /* Free global helper pages */ + mali_free_empty_page(mali_empty_page_directory_phys, mali_empty_page_directory_virt); + mali_empty_page_directory_phys = MALI_INVALID_PAGE; + mali_empty_page_directory_virt = NULL; + + /* Free the page fault flush pages */ + mali_destroy_fault_flush_pages(&mali_page_fault_flush_page_directory, &mali_page_fault_flush_page_directory_mapping, + &mali_page_fault_flush_page_table, &mali_page_fault_flush_page_table_mapping, + &mali_page_fault_flush_data_page, &mali_page_fault_flush_data_page_mapping); +} + +struct mali_mmu_core *mali_mmu_create(_mali_osk_resource_t *resource, struct mali_group *group, mali_bool is_virtual) +{ + struct mali_mmu_core* mmu = NULL; + + MALI_DEBUG_ASSERT_POINTER(resource); + + MALI_DEBUG_PRINT(2, ("Mali MMU: Creating Mali MMU: %s\n", resource->description)); + + mmu = _mali_osk_calloc(1,sizeof(struct mali_mmu_core)); + if (NULL != mmu) { + if (_MALI_OSK_ERR_OK == mali_hw_core_create(&mmu->hw_core, resource, MALI_MMU_REGISTERS_SIZE)) { + if (_MALI_OSK_ERR_OK == mali_group_add_mmu_core(group, mmu)) { + if (is_virtual) { + /* Skip reset and IRQ setup for virtual MMU */ + return mmu; + } + + if (_MALI_OSK_ERR_OK == mali_mmu_reset(mmu)) { + /* Setup IRQ handlers (which will do IRQ probing if needed) */ + mmu->irq = _mali_osk_irq_init(resource->irq, + mali_group_upper_half_mmu, + group, + mali_mmu_probe_trigger, + mali_mmu_probe_ack, + mmu, + resource->description); + if (NULL != mmu->irq) { + return mmu; + } else { + MALI_PRINT_ERROR(("Mali MMU: Failed to setup interrupt handlers for MMU %s\n", mmu->hw_core.description)); + } + } + mali_group_remove_mmu_core(group); + } else { + MALI_PRINT_ERROR(("Mali MMU: Failed to add core %s to group\n", mmu->hw_core.description)); + } + mali_hw_core_delete(&mmu->hw_core); + } + + _mali_osk_free(mmu); + } else { + MALI_PRINT_ERROR(("Failed to allocate memory for MMU\n")); + } + + return NULL; +} + +void mali_mmu_delete(struct mali_mmu_core *mmu) +{ + if (NULL != mmu->irq) { + _mali_osk_irq_term(mmu->irq); + } + + mali_hw_core_delete(&mmu->hw_core); + _mali_osk_free(mmu); +} + +static void mali_mmu_enable_paging(struct mali_mmu_core *mmu) +{ + int i; + + mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_ENABLE_PAGING); + + for (i = 0; i < MALI_REG_POLL_COUNT_FAST; ++i) { + if (mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_STATUS) & MALI_MMU_STATUS_BIT_PAGING_ENABLED) { + break; + } + } + if (MALI_REG_POLL_COUNT_FAST == i) { + MALI_PRINT_ERROR(("Enable paging request failed, MMU status is 0x%08X\n", mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_STATUS))); + } +} + +/** + * Issues the enable stall command to the MMU and waits for HW to complete the request + * @param mmu The MMU to enable paging for + * @return MALI_TRUE if HW stall was successfully engaged, otherwise MALI_FALSE (req timed out) + */ +static mali_bool mali_mmu_enable_stall(struct mali_mmu_core *mmu) +{ + int i; + u32 mmu_status = mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_STATUS); + + if ( 0 == (mmu_status & MALI_MMU_STATUS_BIT_PAGING_ENABLED) ) { + MALI_DEBUG_PRINT(4, ("MMU stall is implicit when Paging is not enabled.\n")); + return MALI_TRUE; + } + + if ( mmu_status & MALI_MMU_STATUS_BIT_PAGE_FAULT_ACTIVE ) { + MALI_DEBUG_PRINT(3, ("Aborting MMU stall request since it is in pagefault state.\n")); + return MALI_FALSE; + } + + mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_ENABLE_STALL); + + for (i = 0; i < MALI_REG_POLL_COUNT_FAST; ++i) { + mmu_status = mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_STATUS); + if (mmu_status & MALI_MMU_STATUS_BIT_PAGE_FAULT_ACTIVE) { + break; + } + if ((mmu_status & MALI_MMU_STATUS_BIT_STALL_ACTIVE) && (0 == (mmu_status & MALI_MMU_STATUS_BIT_STALL_NOT_ACTIVE))) { + break; + } + if (0 == (mmu_status & ( MALI_MMU_STATUS_BIT_PAGING_ENABLED ))) { + break; + } + } + if (MALI_REG_POLL_COUNT_FAST == i) { + MALI_DEBUG_PRINT(2, ("Enable stall request failed, MMU status is 0x%08X\n", mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_STATUS))); + return MALI_FALSE; + } + + if ( mmu_status & MALI_MMU_STATUS_BIT_PAGE_FAULT_ACTIVE ) { + MALI_DEBUG_PRINT(2, ("Aborting MMU stall request since it has a pagefault.\n")); + return MALI_FALSE; + } + + return MALI_TRUE; +} + +/** + * Issues the disable stall command to the MMU and waits for HW to complete the request + * @param mmu The MMU to enable paging for + */ +static void mali_mmu_disable_stall(struct mali_mmu_core *mmu) +{ + int i; + u32 mmu_status = mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_STATUS); + + if ( 0 == (mmu_status & MALI_MMU_STATUS_BIT_PAGING_ENABLED )) { + MALI_DEBUG_PRINT(3, ("MMU disable skipped since it was not enabled.\n")); + return; + } + if (mmu_status & MALI_MMU_STATUS_BIT_PAGE_FAULT_ACTIVE) { + MALI_DEBUG_PRINT(2, ("Aborting MMU disable stall request since it is in pagefault state.\n")); + return; + } + + mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_DISABLE_STALL); + + for (i = 0; i < MALI_REG_POLL_COUNT_FAST; ++i) { + u32 status = mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_STATUS); + if ( 0 == (status & MALI_MMU_STATUS_BIT_STALL_ACTIVE) ) { + break; + } + if ( status & MALI_MMU_STATUS_BIT_PAGE_FAULT_ACTIVE ) { + break; + } + if ( 0 == (mmu_status & MALI_MMU_STATUS_BIT_PAGING_ENABLED )) { + break; + } + } + if (MALI_REG_POLL_COUNT_FAST == i) MALI_DEBUG_PRINT(1,("Disable stall request failed, MMU status is 0x%08X\n", mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_STATUS))); +} + +void mali_mmu_page_fault_done(struct mali_mmu_core *mmu) +{ + MALI_DEBUG_PRINT(4, ("Mali MMU: %s: Leaving page fault mode\n", mmu->hw_core.description)); + mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_PAGE_FAULT_DONE); +} + +MALI_STATIC_INLINE _mali_osk_errcode_t mali_mmu_raw_reset(struct mali_mmu_core *mmu) +{ + int i; + + mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_DTE_ADDR, 0xCAFEBABE); + MALI_DEBUG_ASSERT(0xCAFEB000 == mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_DTE_ADDR)); + mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_HARD_RESET); + + for (i = 0; i < MALI_REG_POLL_COUNT_FAST; ++i) { + if (mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_DTE_ADDR) == 0) { + break; + } + } + if (MALI_REG_POLL_COUNT_FAST == i) { + MALI_PRINT_ERROR(("Reset request failed, MMU status is 0x%08X\n", mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_STATUS))); + return _MALI_OSK_ERR_FAULT; + } + + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t mali_mmu_reset(struct mali_mmu_core *mmu) +{ + _mali_osk_errcode_t err = _MALI_OSK_ERR_FAULT; + mali_bool stall_success; + MALI_DEBUG_ASSERT_POINTER(mmu); + + stall_success = mali_mmu_enable_stall(mmu); + if (!stall_success) { + err = _MALI_OSK_ERR_BUSY; + } + + MALI_DEBUG_PRINT(3, ("Mali MMU: mali_kernel_mmu_reset: %s\n", mmu->hw_core.description)); + + if (_MALI_OSK_ERR_OK == mali_mmu_raw_reset(mmu)) { + mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_INT_MASK, MALI_MMU_INTERRUPT_PAGE_FAULT | MALI_MMU_INTERRUPT_READ_BUS_ERROR); + /* no session is active, so just activate the empty page directory */ + mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_DTE_ADDR, mali_empty_page_directory_phys); + mali_mmu_enable_paging(mmu); + err = _MALI_OSK_ERR_OK; + } + mali_mmu_disable_stall(mmu); + + return err; +} + +mali_bool mali_mmu_zap_tlb(struct mali_mmu_core *mmu) +{ + mali_bool stall_success = mali_mmu_enable_stall(mmu); + + mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_ZAP_CACHE); + + if (MALI_FALSE == stall_success) { + /* False means that it is in Pagefault state. Not possible to disable_stall then */ + return MALI_FALSE; + } + + mali_mmu_disable_stall(mmu); + return MALI_TRUE; +} + +void mali_mmu_zap_tlb_without_stall(struct mali_mmu_core *mmu) +{ + mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_ZAP_CACHE); +} + + +void mali_mmu_invalidate_page(struct mali_mmu_core *mmu, u32 mali_address) +{ + mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_ZAP_ONE_LINE, MALI_MMU_PDE_ENTRY(mali_address)); +} + +static void mali_mmu_activate_address_space(struct mali_mmu_core *mmu, u32 page_directory) +{ + /* The MMU must be in stalled or page fault mode, for this writing to work */ + MALI_DEBUG_ASSERT( 0 != ( mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_STATUS) + & (MALI_MMU_STATUS_BIT_STALL_ACTIVE|MALI_MMU_STATUS_BIT_PAGE_FAULT_ACTIVE) ) ); + mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_DTE_ADDR, page_directory); + mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_ZAP_CACHE); + +} + +void mali_mmu_activate_page_directory(struct mali_mmu_core *mmu, struct mali_page_directory *pagedir) +{ + mali_bool stall_success; + MALI_DEBUG_ASSERT_POINTER(mmu); + + MALI_DEBUG_PRINT(5, ("Asked to activate page directory 0x%x on MMU %s\n", pagedir, mmu->hw_core.description)); + + stall_success = mali_mmu_enable_stall(mmu); + MALI_DEBUG_ASSERT(stall_success); + MALI_IGNORE(stall_success); + mali_mmu_activate_address_space(mmu, pagedir->page_directory); + mali_mmu_disable_stall(mmu); +} + +void mali_mmu_activate_empty_page_directory(struct mali_mmu_core* mmu) +{ + mali_bool stall_success; + + MALI_DEBUG_ASSERT_POINTER(mmu); + MALI_DEBUG_PRINT(3, ("Activating the empty page directory on MMU %s\n", mmu->hw_core.description)); + + stall_success = mali_mmu_enable_stall(mmu); + + /* This function can only be called when the core is idle, so it could not fail. */ + MALI_DEBUG_ASSERT(stall_success); + MALI_IGNORE(stall_success); + + mali_mmu_activate_address_space(mmu, mali_empty_page_directory_phys); + mali_mmu_disable_stall(mmu); +} + +void mali_mmu_activate_fault_flush_page_directory(struct mali_mmu_core* mmu) +{ + mali_bool stall_success; + MALI_DEBUG_ASSERT_POINTER(mmu); + + MALI_DEBUG_PRINT(3, ("Activating the page fault flush page directory on MMU %s\n", mmu->hw_core.description)); + stall_success = mali_mmu_enable_stall(mmu); + /* This function is expect to fail the stalling, since it might be in PageFault mode when it is called */ + mali_mmu_activate_address_space(mmu, mali_page_fault_flush_page_directory); + if ( MALI_TRUE==stall_success ) mali_mmu_disable_stall(mmu); +} + +/* Is called when we want the mmu to give an interrupt */ +static void mali_mmu_probe_trigger(void *data) +{ + struct mali_mmu_core *mmu = (struct mali_mmu_core *)data; + mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_INT_RAWSTAT, MALI_MMU_INTERRUPT_PAGE_FAULT|MALI_MMU_INTERRUPT_READ_BUS_ERROR); +} + +/* Is called when the irq probe wants the mmu to acknowledge an interrupt from the hw */ +static _mali_osk_errcode_t mali_mmu_probe_ack(void *data) +{ + struct mali_mmu_core *mmu = (struct mali_mmu_core *)data; + u32 int_stat; + + int_stat = mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_INT_STATUS); + + MALI_DEBUG_PRINT(2, ("mali_mmu_probe_irq_acknowledge: intstat 0x%x\n", int_stat)); + if (int_stat & MALI_MMU_INTERRUPT_PAGE_FAULT) { + MALI_DEBUG_PRINT(2, ("Probe: Page fault detect: PASSED\n")); + mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_INT_CLEAR, MALI_MMU_INTERRUPT_PAGE_FAULT); + } else { + MALI_DEBUG_PRINT(1, ("Probe: Page fault detect: FAILED\n")); + } + + if (int_stat & MALI_MMU_INTERRUPT_READ_BUS_ERROR) { + MALI_DEBUG_PRINT(2, ("Probe: Bus read error detect: PASSED\n")); + mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_INT_CLEAR, MALI_MMU_INTERRUPT_READ_BUS_ERROR); + } else { + MALI_DEBUG_PRINT(1, ("Probe: Bus read error detect: FAILED\n")); + } + + if ( (int_stat & (MALI_MMU_INTERRUPT_PAGE_FAULT|MALI_MMU_INTERRUPT_READ_BUS_ERROR)) == + (MALI_MMU_INTERRUPT_PAGE_FAULT|MALI_MMU_INTERRUPT_READ_BUS_ERROR)) { + return _MALI_OSK_ERR_OK; + } + + return _MALI_OSK_ERR_FAULT; +} + +#if 0 +void mali_mmu_print_state(struct mali_mmu_core *mmu) +{ + MALI_DEBUG_PRINT(2, ("MMU: State of %s is 0x%08x\n", mmu->hw_core.description, mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_STATUS))); +} +#endif diff --git a/drivers/gpu/arm/mali/common/mali_mmu.h b/drivers/gpu/arm/mali/common/mali_mmu.h new file mode 100644 index 00000000000000..1e04e93867ef73 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_mmu.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_MMU_H__ +#define __MALI_MMU_H__ + +#include "mali_osk.h" +#include "mali_mmu_page_directory.h" +#include "mali_hw_core.h" + +/* Forward declaration from mali_group.h */ +struct mali_group; + +/** + * MMU register numbers + * Used in the register read/write routines. + * See the hardware documentation for more information about each register + */ +typedef enum mali_mmu_register { + MALI_MMU_REGISTER_DTE_ADDR = 0x0000, /**< Current Page Directory Pointer */ + MALI_MMU_REGISTER_STATUS = 0x0004, /**< Status of the MMU */ + MALI_MMU_REGISTER_COMMAND = 0x0008, /**< Command register, used to control the MMU */ + MALI_MMU_REGISTER_PAGE_FAULT_ADDR = 0x000C, /**< Logical address of the last page fault */ + MALI_MMU_REGISTER_ZAP_ONE_LINE = 0x010, /**< Used to invalidate the mapping of a single page from the MMU */ + MALI_MMU_REGISTER_INT_RAWSTAT = 0x0014, /**< Raw interrupt status, all interrupts visible */ + MALI_MMU_REGISTER_INT_CLEAR = 0x0018, /**< Indicate to the MMU that the interrupt has been received */ + MALI_MMU_REGISTER_INT_MASK = 0x001C, /**< Enable/disable types of interrupts */ + MALI_MMU_REGISTER_INT_STATUS = 0x0020 /**< Interrupt status based on the mask */ +} mali_mmu_register; + +/** + * MMU interrupt register bits + * Each cause of the interrupt is reported + * through the (raw) interrupt status registers. + * Multiple interrupts can be pending, so multiple bits + * can be set at once. + */ +typedef enum mali_mmu_interrupt { + MALI_MMU_INTERRUPT_PAGE_FAULT = 0x01, /**< A page fault occured */ + MALI_MMU_INTERRUPT_READ_BUS_ERROR = 0x02 /**< A bus read error occured */ +} mali_mmu_interrupt; + +typedef enum mali_mmu_status_bits { + MALI_MMU_STATUS_BIT_PAGING_ENABLED = 1 << 0, + MALI_MMU_STATUS_BIT_PAGE_FAULT_ACTIVE = 1 << 1, + MALI_MMU_STATUS_BIT_STALL_ACTIVE = 1 << 2, + MALI_MMU_STATUS_BIT_IDLE = 1 << 3, + MALI_MMU_STATUS_BIT_REPLAY_BUFFER_EMPTY = 1 << 4, + MALI_MMU_STATUS_BIT_PAGE_FAULT_IS_WRITE = 1 << 5, + MALI_MMU_STATUS_BIT_STALL_NOT_ACTIVE = 1 << 31, +} mali_mmu_status_bits; + +/** + * Definition of the MMU struct + * Used to track a MMU unit in the system. + * Contains information about the mapping of the registers + */ +struct mali_mmu_core { + struct mali_hw_core hw_core; /**< Common for all HW cores */ + _mali_osk_irq_t *irq; /**< IRQ handler */ +}; + +_mali_osk_errcode_t mali_mmu_initialize(void); + +void mali_mmu_terminate(void); + +struct mali_mmu_core *mali_mmu_create(_mali_osk_resource_t *resource, struct mali_group *group, mali_bool is_virtual); +void mali_mmu_delete(struct mali_mmu_core *mmu); + +_mali_osk_errcode_t mali_mmu_reset(struct mali_mmu_core *mmu); +mali_bool mali_mmu_zap_tlb(struct mali_mmu_core *mmu); +void mali_mmu_zap_tlb_without_stall(struct mali_mmu_core *mmu); +void mali_mmu_invalidate_page(struct mali_mmu_core *mmu, u32 mali_address); + +void mali_mmu_activate_page_directory(struct mali_mmu_core* mmu, struct mali_page_directory *pagedir); +void mali_mmu_activate_empty_page_directory(struct mali_mmu_core* mmu); +void mali_mmu_activate_fault_flush_page_directory(struct mali_mmu_core* mmu); + +void mali_mmu_page_fault_done(struct mali_mmu_core *mmu); + +/*** Register reading/writing functions ***/ +MALI_STATIC_INLINE u32 mali_mmu_get_int_status(struct mali_mmu_core *mmu) +{ + return mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_INT_STATUS); +} + +MALI_STATIC_INLINE u32 mali_mmu_get_rawstat(struct mali_mmu_core *mmu) +{ + return mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_INT_RAWSTAT); +} + +MALI_STATIC_INLINE void mali_mmu_mask_all_interrupts(struct mali_mmu_core *mmu) +{ + mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_INT_MASK, 0); +} + +MALI_STATIC_INLINE u32 mali_mmu_get_status(struct mali_mmu_core *mmu) +{ + return mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_STATUS); +} + +MALI_STATIC_INLINE u32 mali_mmu_get_page_fault_addr(struct mali_mmu_core *mmu) +{ + return mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_PAGE_FAULT_ADDR); +} + +#endif /* __MALI_MMU_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_mmu_page_directory.c b/drivers/gpu/arm/mali/common/mali_mmu_page_directory.c new file mode 100644 index 00000000000000..3e82df24df593b --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_mmu_page_directory.c @@ -0,0 +1,436 @@ +/* + * Copyright (C) 2011-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_uk_types.h" +#include "mali_mmu_page_directory.h" +#include "mali_memory.h" +#include "mali_l2_cache.h" + +static _mali_osk_errcode_t fill_page(mali_io_address mapping, u32 data); + +u32 mali_allocate_empty_page(mali_io_address *virt_addr) +{ + _mali_osk_errcode_t err; + mali_io_address mapping; + u32 address; + + if(_MALI_OSK_ERR_OK != mali_mmu_get_table_page(&address, &mapping)) { + /* Allocation failed */ + MALI_DEBUG_PRINT(2, ("Mali MMU: Failed to get table page for empty pgdir\n")); + return 0; + } + + MALI_DEBUG_ASSERT_POINTER( mapping ); + + err = fill_page(mapping, 0); + if (_MALI_OSK_ERR_OK != err) { + mali_mmu_release_table_page(address, mapping); + MALI_DEBUG_PRINT(2, ("Mali MMU: Failed to zero page\n")); + return 0; + } + + *virt_addr = mapping; + return address; +} + +void mali_free_empty_page(u32 address, mali_io_address virt_addr) +{ + if (MALI_INVALID_PAGE != address) { + mali_mmu_release_table_page(address, virt_addr); + } +} + +_mali_osk_errcode_t mali_create_fault_flush_pages(u32 *page_directory, mali_io_address *page_directory_mapping, + u32 *page_table, mali_io_address *page_table_mapping, + u32 *data_page, mali_io_address *data_page_mapping) +{ + _mali_osk_errcode_t err; + + err = mali_mmu_get_table_page(data_page, data_page_mapping); + if (_MALI_OSK_ERR_OK == err) { + err = mali_mmu_get_table_page(page_table, page_table_mapping); + if (_MALI_OSK_ERR_OK == err) { + err = mali_mmu_get_table_page(page_directory, page_directory_mapping); + if (_MALI_OSK_ERR_OK == err) { + fill_page(*data_page_mapping, 0); + fill_page(*page_table_mapping, *data_page | MALI_MMU_FLAGS_DEFAULT); + fill_page(*page_directory_mapping, *page_table | MALI_MMU_FLAGS_PRESENT); + MALI_SUCCESS; + } + mali_mmu_release_table_page(*page_table, *page_table_mapping); + *page_table = MALI_INVALID_PAGE; + } + mali_mmu_release_table_page(*data_page, *data_page_mapping); + *data_page = MALI_INVALID_PAGE; + } + return err; +} + +void mali_destroy_fault_flush_pages(u32 *page_directory, mali_io_address *page_directory_mapping, + u32 *page_table, mali_io_address *page_table_mapping, + u32 *data_page, mali_io_address *data_page_mapping) +{ + if (MALI_INVALID_PAGE != *page_directory) { + mali_mmu_release_table_page(*page_directory, *page_directory_mapping); + *page_directory = MALI_INVALID_PAGE; + *page_directory_mapping = NULL; + } + + if (MALI_INVALID_PAGE != *page_table) { + mali_mmu_release_table_page(*page_table, *page_table_mapping); + *page_table = MALI_INVALID_PAGE; + *page_table_mapping = NULL; + } + + if (MALI_INVALID_PAGE != *data_page) { + mali_mmu_release_table_page(*data_page, *data_page_mapping); + *data_page = MALI_INVALID_PAGE; + *data_page_mapping = NULL; + } +} + +static _mali_osk_errcode_t fill_page(mali_io_address mapping, u32 data) +{ + int i; + MALI_DEBUG_ASSERT_POINTER( mapping ); + + for(i = 0; i < MALI_MMU_PAGE_SIZE/4; i++) { + _mali_osk_mem_iowrite32_relaxed( mapping, i * sizeof(u32), data); + } + _mali_osk_mem_barrier(); + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_mmu_pagedir_map(struct mali_page_directory *pagedir, u32 mali_address, u32 size) +{ + const int first_pde = MALI_MMU_PDE_ENTRY(mali_address); + const int last_pde = MALI_MMU_PDE_ENTRY(mali_address + size - 1); + _mali_osk_errcode_t err; + mali_io_address pde_mapping; + u32 pde_phys; + int i; + + if (last_pde < first_pde) { + MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + } + + for(i = first_pde; i <= last_pde; i++) { + if(0 == (_mali_osk_mem_ioread32(pagedir->page_directory_mapped, i*sizeof(u32)) & MALI_MMU_FLAGS_PRESENT)) { + /* Page table not present */ + MALI_DEBUG_ASSERT(0 == pagedir->page_entries_usage_count[i]); + MALI_DEBUG_ASSERT(NULL == pagedir->page_entries_mapped[i]); + + err = mali_mmu_get_table_page(&pde_phys, &pde_mapping); + if(_MALI_OSK_ERR_OK != err) { + MALI_PRINT_ERROR(("Failed to allocate page table page.\n")); + return err; + } + pagedir->page_entries_mapped[i] = pde_mapping; + + /* Update PDE, mark as present */ + _mali_osk_mem_iowrite32_relaxed(pagedir->page_directory_mapped, i*sizeof(u32), + pde_phys | MALI_MMU_FLAGS_PRESENT); + + MALI_DEBUG_ASSERT(0 == pagedir->page_entries_usage_count[i]); + pagedir->page_entries_usage_count[i] = 1; + } else { + pagedir->page_entries_usage_count[i]++; + } + } + _mali_osk_write_mem_barrier(); + + MALI_SUCCESS; +} + +MALI_STATIC_INLINE void mali_mmu_zero_pte(mali_io_address page_table, u32 mali_address, u32 size) +{ + int i; + const int first_pte = MALI_MMU_PTE_ENTRY(mali_address); + const int last_pte = MALI_MMU_PTE_ENTRY(mali_address + size - 1); + + for (i = first_pte; i <= last_pte; i++) { + _mali_osk_mem_iowrite32_relaxed(page_table, i * sizeof(u32), 0); + } +} + +_mali_osk_errcode_t mali_mmu_pagedir_unmap(struct mali_page_directory *pagedir, u32 mali_address, u32 size) +{ + const int first_pde = MALI_MMU_PDE_ENTRY(mali_address); + const int last_pde = MALI_MMU_PDE_ENTRY(mali_address + size - 1); + u32 left = size; + int i; + mali_bool pd_changed = MALI_FALSE; + u32 pages_to_invalidate[3]; /* hard-coded to 3: max two pages from the PT level plus max one page from PD level */ + u32 num_pages_inv = 0; + mali_bool invalidate_all = MALI_FALSE; /* safety mechanism in case page_entries_usage_count is unreliable */ + + /* For all page directory entries in range. */ + for (i = first_pde; i <= last_pde; i++) { + u32 size_in_pde, offset; + + MALI_DEBUG_ASSERT_POINTER(pagedir->page_entries_mapped[i]); + MALI_DEBUG_ASSERT(0 != pagedir->page_entries_usage_count[i]); + + /* Offset into page table, 0 if mali_address is 4MiB aligned */ + offset = (mali_address & (MALI_MMU_VIRTUAL_PAGE_SIZE - 1)); + if (left < MALI_MMU_VIRTUAL_PAGE_SIZE - offset) { + size_in_pde = left; + } else { + size_in_pde = MALI_MMU_VIRTUAL_PAGE_SIZE - offset; + } + + pagedir->page_entries_usage_count[i]--; + + /* If entire page table is unused, free it */ + if (0 == pagedir->page_entries_usage_count[i]) { + u32 page_phys; + void *page_virt; + MALI_DEBUG_PRINT(4, ("Releasing page table as this is the last reference\n")); + /* last reference removed, no need to zero out each PTE */ + + page_phys = MALI_MMU_ENTRY_ADDRESS(_mali_osk_mem_ioread32(pagedir->page_directory_mapped, i*sizeof(u32))); + page_virt = pagedir->page_entries_mapped[i]; + pagedir->page_entries_mapped[i] = NULL; + _mali_osk_mem_iowrite32_relaxed(pagedir->page_directory_mapped, i*sizeof(u32), 0); + + mali_mmu_release_table_page(page_phys, page_virt); + pd_changed = MALI_TRUE; + } else { + MALI_DEBUG_ASSERT(num_pages_inv < 2); + if (num_pages_inv < 2) { + pages_to_invalidate[num_pages_inv] = mali_page_directory_get_phys_address(pagedir, i); + num_pages_inv++; + } else { + invalidate_all = MALI_TRUE; + } + + /* If part of the page table is still in use, zero the relevant PTEs */ + mali_mmu_zero_pte(pagedir->page_entries_mapped[i], mali_address, size_in_pde); + } + + left -= size_in_pde; + mali_address += size_in_pde; + } + _mali_osk_write_mem_barrier(); + + /* L2 pages invalidation */ + if (MALI_TRUE == pd_changed) { + MALI_DEBUG_ASSERT(num_pages_inv < 3); + if (num_pages_inv < 3) { + pages_to_invalidate[num_pages_inv] = pagedir->page_directory; + num_pages_inv++; + } else { + invalidate_all = MALI_TRUE; + } + } + + if (invalidate_all) { + mali_l2_cache_invalidate_all(); + } else { + mali_l2_cache_invalidate_all_pages(pages_to_invalidate, num_pages_inv); + } + + MALI_SUCCESS; +} + +struct mali_page_directory *mali_mmu_pagedir_alloc(void) +{ + struct mali_page_directory *pagedir; + + pagedir = _mali_osk_calloc(1, sizeof(struct mali_page_directory)); + if(NULL == pagedir) { + return NULL; + } + + if(_MALI_OSK_ERR_OK != mali_mmu_get_table_page(&pagedir->page_directory, &pagedir->page_directory_mapped)) { + _mali_osk_free(pagedir); + return NULL; + } + + /* Zero page directory */ + fill_page(pagedir->page_directory_mapped, 0); + + return pagedir; +} + +void mali_mmu_pagedir_free(struct mali_page_directory *pagedir) +{ + const int num_page_table_entries = sizeof(pagedir->page_entries_mapped) / sizeof(pagedir->page_entries_mapped[0]); + int i; + + /* Free referenced page tables and zero PDEs. */ + for (i = 0; i < num_page_table_entries; i++) { + if (pagedir->page_directory_mapped && (_mali_osk_mem_ioread32(pagedir->page_directory_mapped, sizeof(u32)*i) & MALI_MMU_FLAGS_PRESENT)) { + u32 phys = _mali_osk_mem_ioread32(pagedir->page_directory_mapped, i*sizeof(u32)) & ~MALI_MMU_FLAGS_MASK; + _mali_osk_mem_iowrite32_relaxed(pagedir->page_directory_mapped, i * sizeof(u32), 0); + mali_mmu_release_table_page(phys, pagedir->page_entries_mapped[i]); + } + } + _mali_osk_write_mem_barrier(); + + /* Free the page directory page. */ + mali_mmu_release_table_page(pagedir->page_directory, pagedir->page_directory_mapped); + + _mali_osk_free(pagedir); +} + + +void mali_mmu_pagedir_update(struct mali_page_directory *pagedir, u32 mali_address, u32 phys_address, u32 size, u32 permission_bits) +{ + u32 end_address = mali_address + size; + + /* Map physical pages into MMU page tables */ + for ( ; mali_address < end_address; mali_address += MALI_MMU_PAGE_SIZE, phys_address += MALI_MMU_PAGE_SIZE) { + MALI_DEBUG_ASSERT_POINTER(pagedir->page_entries_mapped[MALI_MMU_PDE_ENTRY(mali_address)]); + _mali_osk_mem_iowrite32_relaxed(pagedir->page_entries_mapped[MALI_MMU_PDE_ENTRY(mali_address)], + MALI_MMU_PTE_ENTRY(mali_address) * sizeof(u32), + phys_address | permission_bits); + } +} + +u32 mali_page_directory_get_phys_address(struct mali_page_directory *pagedir, u32 index) +{ + return (_mali_osk_mem_ioread32(pagedir->page_directory_mapped, index*sizeof(u32)) & ~MALI_MMU_FLAGS_MASK); +} + +/* For instrumented */ +struct dump_info { + u32 buffer_left; + u32 register_writes_size; + u32 page_table_dump_size; + u32 *buffer; +}; + +static _mali_osk_errcode_t writereg(u32 where, u32 what, const char *comment, struct dump_info *info) +{ + if (NULL != info) { + info->register_writes_size += sizeof(u32)*2; /* two 32-bit words */ + + if (NULL != info->buffer) { + /* check that we have enough space */ + if (info->buffer_left < sizeof(u32)*2) MALI_ERROR(_MALI_OSK_ERR_NOMEM); + + *info->buffer = where; + info->buffer++; + + *info->buffer = what; + info->buffer++; + + info->buffer_left -= sizeof(u32)*2; + } + } + + MALI_SUCCESS; +} + +static _mali_osk_errcode_t mali_mmu_dump_page(mali_io_address page, u32 phys_addr, struct dump_info * info) +{ + if (NULL != info) { + /* 4096 for the page and 4 bytes for the address */ + const u32 page_size_in_elements = MALI_MMU_PAGE_SIZE / 4; + const u32 page_size_in_bytes = MALI_MMU_PAGE_SIZE; + const u32 dump_size_in_bytes = MALI_MMU_PAGE_SIZE + 4; + + info->page_table_dump_size += dump_size_in_bytes; + + if (NULL != info->buffer) { + if (info->buffer_left < dump_size_in_bytes) MALI_ERROR(_MALI_OSK_ERR_NOMEM); + + *info->buffer = phys_addr; + info->buffer++; + + _mali_osk_memcpy(info->buffer, page, page_size_in_bytes); + info->buffer += page_size_in_elements; + + info->buffer_left -= dump_size_in_bytes; + } + } + + MALI_SUCCESS; +} + +static _mali_osk_errcode_t dump_mmu_page_table(struct mali_page_directory *pagedir, struct dump_info * info) +{ + MALI_DEBUG_ASSERT_POINTER(pagedir); + MALI_DEBUG_ASSERT_POINTER(info); + + if (NULL != pagedir->page_directory_mapped) { + int i; + + MALI_CHECK_NO_ERROR( + mali_mmu_dump_page(pagedir->page_directory_mapped, pagedir->page_directory, info) + ); + + for (i = 0; i < 1024; i++) { + if (NULL != pagedir->page_entries_mapped[i]) { + MALI_CHECK_NO_ERROR( + mali_mmu_dump_page(pagedir->page_entries_mapped[i], + _mali_osk_mem_ioread32(pagedir->page_directory_mapped, + i * sizeof(u32)) & ~MALI_MMU_FLAGS_MASK, info) + ); + } + } + } + + MALI_SUCCESS; +} + +static _mali_osk_errcode_t dump_mmu_registers(struct mali_page_directory *pagedir, struct dump_info * info) +{ + MALI_CHECK_NO_ERROR(writereg(0x00000000, pagedir->page_directory, + "set the page directory address", info)); + MALI_CHECK_NO_ERROR(writereg(0x00000008, 4, "zap???", info)); + MALI_CHECK_NO_ERROR(writereg(0x00000008, 0, "enable paging", info)); + MALI_SUCCESS; +} + +_mali_osk_errcode_t _mali_ukk_query_mmu_page_table_dump_size( _mali_uk_query_mmu_page_table_dump_size_s *args ) +{ + struct dump_info info = { 0, 0, 0, NULL }; + struct mali_session_data * session_data; + + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + + session_data = (struct mali_session_data *)(args->ctx); + + MALI_CHECK_NO_ERROR(dump_mmu_registers(session_data->page_directory, &info)); + MALI_CHECK_NO_ERROR(dump_mmu_page_table(session_data->page_directory, &info)); + args->size = info.register_writes_size + info.page_table_dump_size; + MALI_SUCCESS; +} + +_mali_osk_errcode_t _mali_ukk_dump_mmu_page_table( _mali_uk_dump_mmu_page_table_s * args ) +{ + struct dump_info info = { 0, 0, 0, NULL }; + struct mali_session_data * session_data; + + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + MALI_CHECK_NON_NULL(args->buffer, _MALI_OSK_ERR_INVALID_ARGS); + + session_data = (struct mali_session_data *)(args->ctx); + + info.buffer_left = args->size; + info.buffer = args->buffer; + + args->register_writes = info.buffer; + MALI_CHECK_NO_ERROR(dump_mmu_registers(session_data->page_directory, &info)); + + args->page_table_dump = info.buffer; + MALI_CHECK_NO_ERROR(dump_mmu_page_table(session_data->page_directory, &info)); + + args->register_writes_size = info.register_writes_size; + args->page_table_dump_size = info.page_table_dump_size; + + MALI_SUCCESS; +} diff --git a/drivers/gpu/arm/mali/common/mali_mmu_page_directory.h b/drivers/gpu/arm/mali/common/mali_mmu_page_directory.h new file mode 100644 index 00000000000000..2d29458dfefe0e --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_mmu_page_directory.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2011-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_MMU_PAGE_DIRECTORY_H__ +#define __MALI_MMU_PAGE_DIRECTORY_H__ + +#include "mali_osk.h" + +/** + * Size of an MMU page in bytes + */ +#define MALI_MMU_PAGE_SIZE 0x1000 + +/* + * Size of the address space referenced by a page table page + */ +#define MALI_MMU_VIRTUAL_PAGE_SIZE 0x400000 /* 4 MiB */ + +/** + * Page directory index from address + * Calculates the page directory index from the given address + */ +#define MALI_MMU_PDE_ENTRY(address) (((address)>>22) & 0x03FF) + +/** + * Page table index from address + * Calculates the page table index from the given address + */ +#define MALI_MMU_PTE_ENTRY(address) (((address)>>12) & 0x03FF) + +/** + * Extract the memory address from an PDE/PTE entry + */ +#define MALI_MMU_ENTRY_ADDRESS(value) ((value) & 0xFFFFFC00) + +#define MALI_INVALID_PAGE ((u32)(~0)) + +/** + * + */ +typedef enum mali_mmu_entry_flags { + MALI_MMU_FLAGS_PRESENT = 0x01, + MALI_MMU_FLAGS_READ_PERMISSION = 0x02, + MALI_MMU_FLAGS_WRITE_PERMISSION = 0x04, + MALI_MMU_FLAGS_OVERRIDE_CACHE = 0x8, + MALI_MMU_FLAGS_WRITE_CACHEABLE = 0x10, + MALI_MMU_FLAGS_WRITE_ALLOCATE = 0x20, + MALI_MMU_FLAGS_WRITE_BUFFERABLE = 0x40, + MALI_MMU_FLAGS_READ_CACHEABLE = 0x80, + MALI_MMU_FLAGS_READ_ALLOCATE = 0x100, + MALI_MMU_FLAGS_MASK = 0x1FF, +} mali_mmu_entry_flags; + + +#define MALI_MMU_FLAGS_FORCE_GP_READ_ALLOCATE ( \ +MALI_MMU_FLAGS_PRESENT | \ + MALI_MMU_FLAGS_READ_PERMISSION | \ + MALI_MMU_FLAGS_WRITE_PERMISSION | \ + MALI_MMU_FLAGS_OVERRIDE_CACHE | \ + MALI_MMU_FLAGS_WRITE_CACHEABLE | \ + MALI_MMU_FLAGS_WRITE_BUFFERABLE | \ + MALI_MMU_FLAGS_READ_CACHEABLE | \ + MALI_MMU_FLAGS_READ_ALLOCATE ) + +#define MALI_MMU_FLAGS_DEFAULT ( \ + MALI_MMU_FLAGS_PRESENT | \ + MALI_MMU_FLAGS_READ_PERMISSION | \ + MALI_MMU_FLAGS_WRITE_PERMISSION ) + + +struct mali_page_directory { + u32 page_directory; /**< Physical address of the memory session's page directory */ + mali_io_address page_directory_mapped; /**< Pointer to the mapped version of the page directory into the kernel's address space */ + + mali_io_address page_entries_mapped[1024]; /**< Pointers to the page tables which exists in the page directory mapped into the kernel's address space */ + u32 page_entries_usage_count[1024]; /**< Tracks usage count of the page table pages, so they can be releases on the last reference */ +}; + +/* Map Mali virtual address space (i.e. ensure page tables exist for the virtual range) */ +_mali_osk_errcode_t mali_mmu_pagedir_map(struct mali_page_directory *pagedir, u32 mali_address, u32 size); +_mali_osk_errcode_t mali_mmu_pagedir_unmap(struct mali_page_directory *pagedir, u32 mali_address, u32 size); + +/* Back virtual address space with actual pages. Assumes input is contiguous and 4k aligned. */ +void mali_mmu_pagedir_update(struct mali_page_directory *pagedir, u32 mali_address, u32 phys_address, u32 size, u32 cache_settings); + +u32 mali_page_directory_get_phys_address(struct mali_page_directory *pagedir, u32 index); + +u32 mali_allocate_empty_page(mali_io_address *virtual); +void mali_free_empty_page(u32 address, mali_io_address virtual); +_mali_osk_errcode_t mali_create_fault_flush_pages(u32 *page_directory, mali_io_address *page_directory_mapping, + u32 *page_table, mali_io_address *page_table_mapping, + u32 *data_page, mali_io_address *data_page_mapping); +void mali_destroy_fault_flush_pages(u32 *page_directory, mali_io_address *page_directory_mapping, + u32 *page_table, mali_io_address *page_table_mapping, + u32 *data_page, mali_io_address *data_page_mapping); + +struct mali_page_directory *mali_mmu_pagedir_alloc(void); +void mali_mmu_pagedir_free(struct mali_page_directory *pagedir); + +#endif /* __MALI_MMU_PAGE_DIRECTORY_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_osk.h b/drivers/gpu/arm/mali/common/mali_osk.h new file mode 100644 index 00000000000000..5a9cf36fbe223e --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_osk.h @@ -0,0 +1,1335 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk.h + * Defines the OS abstraction layer for the kernel device driver (OSK) + */ + +#ifndef __MALI_OSK_H__ +#define __MALI_OSK_H__ + +#include "mali_osk_types.h" +#include "mali_osk_specific.h" /* include any per-os specifics */ +#include "mali_osk_locks.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup uddapi Unified Device Driver (UDD) APIs + * + * @{ + */ + +/** + * @addtogroup oskapi UDD OS Abstraction for Kernel-side (OSK) APIs + * + * @{ + */ + +/** @addtogroup _mali_osk_lock OSK Mutual Exclusion Locks + * @{ */ + +#ifdef DEBUG +/** @brief Macro for asserting that the current thread holds a given lock + */ +#define MALI_DEBUG_ASSERT_LOCK_HELD(l) MALI_DEBUG_ASSERT(_mali_osk_lock_get_owner((_mali_osk_lock_debug_t *)l) == _mali_osk_get_tid()); + +/** @brief returns a lock's owner (thread id) if debugging is enabled + */ +#else +#define MALI_DEBUG_ASSERT_LOCK_HELD(l) do {} while(0) +#endif + +/** @} */ /* end group _mali_osk_lock */ + +/** @addtogroup _mali_osk_miscellaneous + * @{ */ + +/** @brief Find the containing structure of another structure + * + * This is the reverse of the operation 'offsetof'. This means that the + * following condition is satisfied: + * + * ptr == _MALI_OSK_CONTAINER_OF( &ptr->member, type, member ) + * + * When ptr is of type 'type'. + * + * Its purpose it to recover a larger structure that has wrapped a smaller one. + * + * @note no type or memory checking occurs to ensure that a wrapper structure + * does in fact exist, and that it is being recovered with respect to the + * correct member. + * + * @param ptr the pointer to the member that is contained within the larger + * structure + * @param type the type of the structure that contains the member + * @param member the name of the member in the structure that ptr points to. + * @return a pointer to a \a type object which contains \a member, as pointed + * to by \a ptr. + */ +#define _MALI_OSK_CONTAINER_OF(ptr, type, member) \ + ((type *)( ((char *)ptr) - offsetof(type,member) )) + +/** @addtogroup _mali_osk_wq + * @{ */ + +/** @brief Initialize work queues (for deferred work) + * + * @return _MALI_OSK_ERR_OK on success, otherwise failure. + */ +_mali_osk_errcode_t _mali_osk_wq_init(void); + +/** @brief Terminate work queues (for deferred work) + */ +void _mali_osk_wq_term(void); + +/** @brief Create work in the work queue + * + * Creates a work object which can be scheduled in the work queue. When + * scheduled, \a handler will be called with \a data as the argument. + * + * Refer to \ref _mali_osk_wq_schedule_work() for details on how work + * is scheduled in the queue. + * + * The returned pointer must be freed with \ref _mali_osk_wq_delete_work() + * when no longer needed. + */ +_mali_osk_wq_work_t *_mali_osk_wq_create_work( _mali_osk_wq_work_handler_t handler, void *data ); + +/** @brief A high priority version of \a _mali_osk_wq_create_work() + * + * Creates a work object which can be scheduled in the high priority work queue. + * + * This is unfortunately needed to get low latency scheduling of the Mali cores. Normally we would + * schedule the next job in hw_irq or tasklet, but often we can't since we need to synchronously map + * and unmap shared memory when a job is connected to external fences (timelines). And this requires + * taking a mutex. + * + * We do signal a lot of other (low priority) work also as part of the job being finished, and if we + * don't set this Mali scheduling thread as high priority, we see that the CPU scheduler often runs + * random things instead of starting the next GPU job when the GPU is idle. So setting the gpu + * scheduler to high priority does give a visually more responsive system. + * + * Start the high priority work with: \a _mali_osk_wq_schedule_work_high_pri() + */ +_mali_osk_wq_work_t *_mali_osk_wq_create_work_high_pri( _mali_osk_wq_work_handler_t handler, void *data ); + +/** @brief Delete a work object + * + * This will flush the work queue to ensure that the work handler will not + * be called after deletion. + */ +void _mali_osk_wq_delete_work( _mali_osk_wq_work_t *work ); + +/** @brief Delete a work object + * + * This will NOT flush the work queue, so only call this if you are sure that the work handler will + * not be called after deletion. + */ +void _mali_osk_wq_delete_work_nonflush( _mali_osk_wq_work_t *work ); + +/** @brief Cause a queued, deferred call of the work handler + * + * _mali_osk_wq_schedule_work provides a mechanism for enqueuing deferred calls + * to the work handler. After calling \ref _mali_osk_wq_schedule_work(), the + * work handler will be scheduled to run at some point in the future. + * + * Typically this is called by the IRQ upper-half to defer further processing of + * IRQ-related work to the IRQ bottom-half handler. This is necessary for work + * that cannot be done in an IRQ context by the IRQ upper-half handler. Timer + * callbacks also use this mechanism, because they are treated as though they + * operate in an IRQ context. Refer to \ref _mali_osk_timer_t for more + * information. + * + * Code that operates in a kernel-process context (with no IRQ context + * restrictions) may also enqueue deferred calls to the IRQ bottom-half. The + * advantage over direct calling is that deferred calling allows the caller and + * IRQ bottom half to hold the same mutex, with a guarantee that they will not + * deadlock just by using this mechanism. + * + * _mali_osk_wq_schedule_work() places deferred call requests on a queue, to + * allow for more than one thread to make a deferred call. Therfore, if it is + * called 'K' times, then the IRQ bottom-half will be scheduled 'K' times too. + * 'K' is a number that is implementation-specific. + * + * _mali_osk_wq_schedule_work() is guaranteed to not block on: + * - enqueuing a deferred call request. + * - the completion of the work handler. + * + * This is to prevent deadlock. For example, if _mali_osk_wq_schedule_work() + * blocked, then it would cause a deadlock when the following two conditions + * hold: + * - The work handler callback (of type _mali_osk_wq_work_handler_t) locks + * a mutex + * - And, at the same time, the caller of _mali_osk_wq_schedule_work() also + * holds the same mutex + * + * @note care must be taken to not overflow the queue that + * _mali_osk_wq_schedule_work() operates on. Code must be structured to + * ensure that the number of requests made to the queue is bounded. Otherwise, + * work will be lost. + * + * The queue that _mali_osk_wq_schedule_work implements is a FIFO of N-writer, + * 1-reader type. The writers are the callers of _mali_osk_wq_schedule_work + * (all OSK-registered IRQ upper-half handlers in the system, watchdog timers, + * callers from a Kernel-process context). The reader is a single thread that + * handles all OSK-registered work. + * + * @param work a pointer to the _mali_osk_wq_work_t object corresponding to the + * work to begin processing. + */ +void _mali_osk_wq_schedule_work( _mali_osk_wq_work_t *work ); + +/** @brief Cause a queued, deferred call of the high priority work handler + * + * Function is the same as \a _mali_osk_wq_schedule_work() with the only + * difference that it runs in a high (real time) priority on the system. + * + * Should only be used as a substitue for doing the same work in interrupts. + * + * This is allowed to sleep, but the work should be small since it will block + * all other applications. +*/ +void _mali_osk_wq_schedule_work_high_pri( _mali_osk_wq_work_t *work ); + +/** @brief Flush the work queue + * + * This will flush the OSK work queue, ensuring all work in the queue has + * completed before returning. + * + * Since this blocks on the completion of work in the work-queue, the + * caller of this function \b must \b not hold any mutexes that are taken by + * any registered work handler. To do so may cause a deadlock. + * + */ +void _mali_osk_wq_flush(void); + +/** @brief Create work in the delayed work queue + * + * Creates a work object which can be scheduled in the work queue. When + * scheduled, a timer will be start and the \a handler will be called with + * \a data as the argument when timer out + * + * Refer to \ref _mali_osk_wq_delayed_schedule_work() for details on how work + * is scheduled in the queue. + * + * The returned pointer must be freed with \ref _mali_osk_wq_delayed_delete_work_nonflush() + * when no longer needed. + */ +_mali_osk_wq_delayed_work_t *_mali_osk_wq_delayed_create_work(_mali_osk_wq_work_handler_t handler, void *data); + +/** @brief Delete a work object + * + * This will NOT flush the work queue, so only call this if you are sure that the work handler will + * not be called after deletion. + */ +void _mali_osk_wq_delayed_delete_work_nonflush(_mali_osk_wq_delayed_work_t *work); + +/** @brief Cancel a delayed work without waiting for it to finish + * + * Note that the \a work callback function may still be running on return from + * _mali_osk_wq_delayed_cancel_work_async(). + * + * @param work The delayed work to be cancelled + */ +void _mali_osk_wq_delayed_cancel_work_async(_mali_osk_wq_delayed_work_t *work); + +/** @brief Cancel a delayed work and wait for it to finish + * + * When this function returns, the \a work was either cancelled or it finished running. + * + * @param work The delayed work to be cancelled + */ +void _mali_osk_wq_delayed_cancel_work_sync(_mali_osk_wq_delayed_work_t *work); + +/** @brief Put \a work task in global workqueue after delay + * + * After waiting for a given time this puts a job in the kernel-global + * workqueue. + * + * If \a work was already on a queue, this function will return without doing anything + * + * @param work job to be done + * @param delay number of jiffies to wait or 0 for immediate execution + */ +void _mali_osk_wq_delayed_schedule_work(_mali_osk_wq_delayed_work_t *work, u32 delay); + +/** @} */ /* end group _mali_osk_wq */ + + +/** @addtogroup _mali_osk_irq + * @{ */ + +/** @brief Initialize IRQ handling for a resource + * + * Registers an interrupt handler \a uhandler for the given IRQ number \a irqnum. + * \a data will be passed as argument to the handler when an interrupt occurs. + * + * If \a irqnum is -1, _mali_osk_irq_init will probe for the IRQ number using + * the supplied \a trigger_func and \a ack_func. These functions will also + * receive \a data as their argument. + * + * @param irqnum The IRQ number that the resource uses, as seen by the CPU. + * The value -1 has a special meaning which indicates the use of probing, and + * trigger_func and ack_func must be non-NULL. + * @param uhandler The interrupt handler, corresponding to a ISR handler for + * the resource + * @param int_data resource specific data, which will be passed to uhandler + * @param trigger_func Optional: a function to trigger the resource's irq, to + * probe for the interrupt. Use NULL if irqnum != -1. + * @param ack_func Optional: a function to acknowledge the resource's irq, to + * probe for the interrupt. Use NULL if irqnum != -1. + * @param probe_data resource-specific data, which will be passed to + * (if present) trigger_func and ack_func + * @param description textual description of the IRQ resource. + * @return on success, a pointer to a _mali_osk_irq_t object, which represents + * the IRQ handling on this resource. NULL on failure. + */ +_mali_osk_irq_t *_mali_osk_irq_init( u32 irqnum, _mali_osk_irq_uhandler_t uhandler, void *int_data, _mali_osk_irq_trigger_t trigger_func, _mali_osk_irq_ack_t ack_func, void *probe_data, const char *description ); + +/** @brief Terminate IRQ handling on a resource. + * + * This will disable the interrupt from the device, and then waits for any + * currently executing IRQ handlers to complete. + * + * @note If work is deferred to an IRQ bottom-half handler through + * \ref _mali_osk_wq_schedule_work(), be sure to flush any remaining work + * with \ref _mali_osk_wq_flush() or (implicitly) with \ref _mali_osk_wq_delete_work() + * + * @param irq a pointer to the _mali_osk_irq_t object corresponding to the + * resource whose IRQ handling is to be terminated. + */ +void _mali_osk_irq_term( _mali_osk_irq_t *irq ); + +/** @} */ /* end group _mali_osk_irq */ + + +/** @addtogroup _mali_osk_atomic + * @{ */ + +/** @brief Decrement an atomic counter + * + * @note It is an error to decrement the counter beyond -(1<<23) + * + * @param atom pointer to an atomic counter */ +void _mali_osk_atomic_dec( _mali_osk_atomic_t *atom ); + +/** @brief Decrement an atomic counter, return new value + * + * @param atom pointer to an atomic counter + * @return The new value, after decrement */ +u32 _mali_osk_atomic_dec_return( _mali_osk_atomic_t *atom ); + +/** @brief Increment an atomic counter + * + * @note It is an error to increment the counter beyond (1<<23)-1 + * + * @param atom pointer to an atomic counter */ +void _mali_osk_atomic_inc( _mali_osk_atomic_t *atom ); + +/** @brief Increment an atomic counter, return new value + * + * @param atom pointer to an atomic counter */ +u32 _mali_osk_atomic_inc_return( _mali_osk_atomic_t *atom ); + +/** @brief Initialize an atomic counter + * + * @note the parameter required is a u32, and so signed integers should be + * cast to u32. + * + * @param atom pointer to an atomic counter + * @param val the value to initialize the atomic counter. + * @return _MALI_OSK_ERR_OK on success, otherwise, a suitable + * _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_osk_atomic_init( _mali_osk_atomic_t *atom, u32 val ); + +/** @brief Read a value from an atomic counter + * + * This can only be safely used to determine the value of the counter when it + * is guaranteed that other threads will not be modifying the counter. This + * makes its usefulness limited. + * + * @param atom pointer to an atomic counter + */ +u32 _mali_osk_atomic_read( _mali_osk_atomic_t *atom ); + +/** @brief Terminate an atomic counter + * + * @param atom pointer to an atomic counter + */ +void _mali_osk_atomic_term( _mali_osk_atomic_t *atom ); + +/** @brief Assign a new val to atomic counter, and return the old atomic counter + * + * @param atom pointer to an atomic counter + * @param val the new value assign to the atomic counter + * @return the old value of the atomic counter + */ +u32 _mali_osk_atomic_xchg( _mali_osk_atomic_t *atom, u32 val ); +/** @} */ /* end group _mali_osk_atomic */ + + +/** @defgroup _mali_osk_memory OSK Memory Allocation + * @{ */ + +/** @brief Allocate zero-initialized memory. + * + * Returns a buffer capable of containing at least \a n elements of \a size + * bytes each. The buffer is initialized to zero. + * + * If there is a need for a bigger block of memory (16KB or bigger), then + * consider to use _mali_osk_vmalloc() instead, as this function might + * map down to a OS function with size limitations. + * + * The buffer is suitably aligned for storage and subsequent access of every + * type that the compiler supports. Therefore, the pointer to the start of the + * buffer may be cast into any pointer type, and be subsequently accessed from + * such a pointer, without loss of information. + * + * When the buffer is no longer in use, it must be freed with _mali_osk_free(). + * Failure to do so will cause a memory leak. + * + * @note Most toolchains supply memory allocation functions that meet the + * compiler's alignment requirements. + * + * @param n Number of elements to allocate + * @param size Size of each element + * @return On success, the zero-initialized buffer allocated. NULL on failure + */ +void *_mali_osk_calloc( u32 n, u32 size ); + +/** @brief Allocate memory. + * + * Returns a buffer capable of containing at least \a size bytes. The + * contents of the buffer are undefined. + * + * If there is a need for a bigger block of memory (16KB or bigger), then + * consider to use _mali_osk_vmalloc() instead, as this function might + * map down to a OS function with size limitations. + * + * The buffer is suitably aligned for storage and subsequent access of every + * type that the compiler supports. Therefore, the pointer to the start of the + * buffer may be cast into any pointer type, and be subsequently accessed from + * such a pointer, without loss of information. + * + * When the buffer is no longer in use, it must be freed with _mali_osk_free(). + * Failure to do so will cause a memory leak. + * + * @note Most toolchains supply memory allocation functions that meet the + * compiler's alignment requirements. + * + * Remember to free memory using _mali_osk_free(). + * @param size Number of bytes to allocate + * @return On success, the buffer allocated. NULL on failure. + */ +void *_mali_osk_malloc( u32 size ); + +/** @brief Free memory. + * + * Reclaims the buffer pointed to by the parameter \a ptr for the system. + * All memory returned from _mali_osk_malloc() and _mali_osk_calloc() + * must be freed before the application exits. Otherwise, + * a memory leak will occur. + * + * Memory must be freed once. It is an error to free the same non-NULL pointer + * more than once. + * + * It is legal to free the NULL pointer. + * + * @param ptr Pointer to buffer to free + */ +void _mali_osk_free( void *ptr ); + +/** @brief Allocate memory. + * + * Returns a buffer capable of containing at least \a size bytes. The + * contents of the buffer are undefined. + * + * This function is potentially slower than _mali_osk_malloc() and _mali_osk_calloc(), + * but do support bigger sizes. + * + * The buffer is suitably aligned for storage and subsequent access of every + * type that the compiler supports. Therefore, the pointer to the start of the + * buffer may be cast into any pointer type, and be subsequently accessed from + * such a pointer, without loss of information. + * + * When the buffer is no longer in use, it must be freed with _mali_osk_free(). + * Failure to do so will cause a memory leak. + * + * @note Most toolchains supply memory allocation functions that meet the + * compiler's alignment requirements. + * + * Remember to free memory using _mali_osk_free(). + * @param size Number of bytes to allocate + * @return On success, the buffer allocated. NULL on failure. + */ +void *_mali_osk_valloc( u32 size ); + +/** @brief Free memory. + * + * Reclaims the buffer pointed to by the parameter \a ptr for the system. + * All memory returned from _mali_osk_valloc() must be freed before the + * application exits. Otherwise a memory leak will occur. + * + * Memory must be freed once. It is an error to free the same non-NULL pointer + * more than once. + * + * It is legal to free the NULL pointer. + * + * @param ptr Pointer to buffer to free + */ +void _mali_osk_vfree( void *ptr ); + +/** @brief Copies memory. + * + * Copies the \a len bytes from the buffer pointed by the parameter \a src + * directly to the buffer pointed by \a dst. + * + * It is an error for \a src to overlap \a dst anywhere in \a len bytes. + * + * @param dst Pointer to the destination array where the content is to be + * copied. + * @param src Pointer to the source of data to be copied. + * @param len Number of bytes to copy. + * @return \a dst is always passed through unmodified. + */ +void *_mali_osk_memcpy( void *dst, const void *src, u32 len ); + +/** @brief Fills memory. + * + * Sets the first \a n bytes of the block of memory pointed to by \a s to + * the specified value + * @param s Pointer to the block of memory to fill. + * @param c Value to be set, passed as u32. Only the 8 Least Significant Bits (LSB) + * are used. + * @param n Number of bytes to be set to the value. + * @return \a s is always passed through unmodified + */ +void *_mali_osk_memset( void *s, u32 c, u32 n ); +/** @} */ /* end group _mali_osk_memory */ + + +/** @brief Checks the amount of memory allocated + * + * Checks that not more than \a max_allocated bytes are allocated. + * + * Some OS bring up an interactive out of memory dialogue when the + * system runs out of memory. This can stall non-interactive + * apps (e.g. automated test runs). This function can be used to + * not trigger the OOM dialogue by keeping allocations + * within a certain limit. + * + * @return MALI_TRUE when \a max_allocated bytes are not in use yet. MALI_FALSE + * when at least \a max_allocated bytes are in use. + */ +mali_bool _mali_osk_mem_check_allocated( u32 max_allocated ); + + +/** @addtogroup _mali_osk_low_level_memory + * @{ */ + +/** @brief Issue a memory barrier + * + * This defines an arbitrary memory barrier operation, which forces an ordering constraint + * on memory read and write operations. + */ +void _mali_osk_mem_barrier( void ); + +/** @brief Issue a write memory barrier + * + * This defines an write memory barrier operation which forces an ordering constraint + * on memory write operations. + */ +void _mali_osk_write_mem_barrier( void ); + +/** @brief Map a physically contiguous region into kernel space + * + * This is primarily used for mapping in registers from resources, and Mali-MMU + * page tables. The mapping is only visable from kernel-space. + * + * Access has to go through _mali_osk_mem_ioread32 and _mali_osk_mem_iowrite32 + * + * @param phys CPU-physical base address of the memory to map in. This must + * be aligned to the system's page size, which is assumed to be 4K. + * @param size the number of bytes of physically contiguous address space to + * map in + * @param description A textual description of the memory being mapped in. + * @return On success, a Mali IO address through which the mapped-in + * memory/registers can be accessed. NULL on failure. + */ +mali_io_address _mali_osk_mem_mapioregion( u32 phys, u32 size, const char *description ); + +/** @brief Unmap a physically contiguous address range from kernel space. + * + * The address range should be one previously mapped in through + * _mali_osk_mem_mapioregion. + * + * It is a programming error to do (but not limited to) the following: + * - attempt an unmap twice + * - unmap only part of a range obtained through _mali_osk_mem_mapioregion + * - unmap more than the range obtained through _mali_osk_mem_mapioregion + * - unmap an address range that was not successfully mapped using + * _mali_osk_mem_mapioregion + * - provide a mapping that does not map to phys. + * + * @param phys CPU-physical base address of the memory that was originally + * mapped in. This must be aligned to the system's page size, which is assumed + * to be 4K + * @param size The number of bytes that were originally mapped in. + * @param mapping The Mali IO address through which the mapping is + * accessed. + */ +void _mali_osk_mem_unmapioregion( u32 phys, u32 size, mali_io_address mapping ); + +/** @brief Allocate and Map a physically contiguous region into kernel space + * + * This is used for allocating physically contiguous regions (such as Mali-MMU + * page tables) and mapping them into kernel space. The mapping is only + * visible from kernel-space. + * + * The alignment of the returned memory is guaranteed to be at least + * _MALI_OSK_CPU_PAGE_SIZE. + * + * Access must go through _mali_osk_mem_ioread32 and _mali_osk_mem_iowrite32 + * + * @note This function is primarily to provide support for OSs that are + * incapable of separating the tasks 'allocate physically contiguous memory' + * and 'map it into kernel space' + * + * @param[out] phys CPU-physical base address of memory that was allocated. + * (*phys) will be guaranteed to be aligned to at least + * _MALI_OSK_CPU_PAGE_SIZE on success. + * + * @param[in] size the number of bytes of physically contiguous memory to + * allocate. This must be a multiple of _MALI_OSK_CPU_PAGE_SIZE. + * + * @return On success, a Mali IO address through which the mapped-in + * memory/registers can be accessed. NULL on failure, and (*phys) is unmodified. + */ +mali_io_address _mali_osk_mem_allocioregion( u32 *phys, u32 size ); + +/** @brief Free a physically contiguous address range from kernel space. + * + * The address range should be one previously mapped in through + * _mali_osk_mem_allocioregion. + * + * It is a programming error to do (but not limited to) the following: + * - attempt a free twice on the same ioregion + * - free only part of a range obtained through _mali_osk_mem_allocioregion + * - free more than the range obtained through _mali_osk_mem_allocioregion + * - free an address range that was not successfully mapped using + * _mali_osk_mem_allocioregion + * - provide a mapping that does not map to phys. + * + * @param phys CPU-physical base address of the memory that was originally + * mapped in, which was aligned to _MALI_OSK_CPU_PAGE_SIZE. + * @param size The number of bytes that were originally mapped in, which was + * a multiple of _MALI_OSK_CPU_PAGE_SIZE. + * @param mapping The Mali IO address through which the mapping is + * accessed. + */ +void _mali_osk_mem_freeioregion( u32 phys, u32 size, mali_io_address mapping ); + +/** @brief Request a region of physically contiguous memory + * + * This is used to ensure exclusive access to a region of physically contigous + * memory. + * + * It is acceptable to implement this as a stub. However, it is then the job + * of the System Integrator to ensure that no other device driver will be using + * the physical address ranges used by Mali, while the Mali device driver is + * loaded. + * + * @param phys CPU-physical base address of the memory to request. This must + * be aligned to the system's page size, which is assumed to be 4K. + * @param size the number of bytes of physically contiguous address space to + * request. + * @param description A textual description of the memory being requested. + * @return _MALI_OSK_ERR_OK on success. Otherwise, a suitable + * _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_osk_mem_reqregion( u32 phys, u32 size, const char *description ); + +/** @brief Un-request a region of physically contiguous memory + * + * This is used to release a regious of physically contiguous memory previously + * requested through _mali_osk_mem_reqregion, so that other device drivers may + * use it. This will be called at time of Mali device driver termination. + * + * It is a programming error to attempt to: + * - unrequest a region twice + * - unrequest only part of a range obtained through _mali_osk_mem_reqregion + * - unrequest more than the range obtained through _mali_osk_mem_reqregion + * - unrequest an address range that was not successfully requested using + * _mali_osk_mem_reqregion + * + * @param phys CPU-physical base address of the memory to un-request. This must + * be aligned to the system's page size, which is assumed to be 4K + * @param size the number of bytes of physically contiguous address space to + * un-request. + */ +void _mali_osk_mem_unreqregion( u32 phys, u32 size ); + +/** @brief Read from a location currently mapped in through + * _mali_osk_mem_mapioregion + * + * This reads a 32-bit word from a 32-bit aligned location. It is a programming + * error to provide unaligned locations, or to read from memory that is not + * mapped in, or not mapped through either _mali_osk_mem_mapioregion() or + * _mali_osk_mem_allocioregion(). + * + * @param mapping Mali IO address to read from + * @param offset Byte offset from the given IO address to operate on, must be a multiple of 4 + * @return the 32-bit word from the specified location. + */ +u32 _mali_osk_mem_ioread32( volatile mali_io_address mapping, u32 offset ); + +/** @brief Write to a location currently mapped in through + * _mali_osk_mem_mapioregion without memory barriers + * + * This write a 32-bit word to a 32-bit aligned location without using memory barrier. + * It is a programming error to provide unaligned locations, or to write to memory that is not + * mapped in, or not mapped through either _mali_osk_mem_mapioregion() or + * _mali_osk_mem_allocioregion(). + * + * @param mapping Mali IO address to write to + * @param offset Byte offset from the given IO address to operate on, must be a multiple of 4 + * @param val the 32-bit word to write. + */ +void _mali_osk_mem_iowrite32_relaxed( volatile mali_io_address addr, u32 offset, u32 val ); + +/** @brief Write to a location currently mapped in through + * _mali_osk_mem_mapioregion with write memory barrier + * + * This write a 32-bit word to a 32-bit aligned location. It is a programming + * error to provide unaligned locations, or to write to memory that is not + * mapped in, or not mapped through either _mali_osk_mem_mapioregion() or + * _mali_osk_mem_allocioregion(). + * + * @param mapping Mali IO address to write to + * @param offset Byte offset from the given IO address to operate on, must be a multiple of 4 + * @param val the 32-bit word to write. + */ +void _mali_osk_mem_iowrite32( volatile mali_io_address mapping, u32 offset, u32 val ); + +/** @brief Flush all CPU caches + * + * This should only be implemented if flushing of the cache is required for + * memory mapped in through _mali_osk_mem_mapregion. + */ +void _mali_osk_cache_flushall( void ); + +/** @brief Flush any caches necessary for the CPU and MALI to have the same view of a range of uncached mapped memory + * + * This should only be implemented if your OS doesn't do a full cache flush (inner & outer) + * after allocating uncached mapped memory. + * + * Some OS do not perform a full cache flush (including all outer caches) for uncached mapped memory. + * They zero the memory through a cached mapping, then flush the inner caches but not the outer caches. + * This is required for MALI to have the correct view of the memory. + */ +void _mali_osk_cache_ensure_uncached_range_flushed( void *uncached_mapping, u32 offset, u32 size ); + +/** @} */ /* end group _mali_osk_low_level_memory */ + + +/** @addtogroup _mali_osk_notification + * + * User space notification framework + * + * Communication with user space of asynchronous events is performed through a + * synchronous call to the \ref u_k_api. + * + * Since the events are asynchronous, the events have to be queued until a + * synchronous U/K API call can be made by user-space. A U/K API call might also + * be received before any event has happened. Therefore the notifications the + * different subsystems wants to send to user space has to be queued for later + * reception, or a U/K API call has to be blocked until an event has occured. + * + * Typical uses of notifications are after running of jobs on the hardware or + * when changes to the system is detected that needs to be relayed to user + * space. + * + * After an event has occured user space has to be notified using some kind of + * message. The notification framework supports sending messages to waiting + * threads or queueing of messages until a U/K API call is made. + * + * The notification queue is a FIFO. There are no restrictions on the numbers + * of readers or writers in the queue. + * + * A message contains what user space needs to identifiy how to handle an + * event. This includes a type field and a possible type specific payload. + * + * A notification to user space is represented by a + * \ref _mali_osk_notification_t object. A sender gets hold of such an object + * using _mali_osk_notification_create(). The buffer given by the + * _mali_osk_notification_t::result_buffer field in the object is used to store + * any type specific data. The other fields are internal to the queue system + * and should not be touched. + * + * @{ */ + +/** @brief Create a notification object + * + * Returns a notification object which can be added to the queue of + * notifications pending for user space transfer. + * + * The implementation will initialize all members of the + * \ref _mali_osk_notification_t object. In particular, the + * _mali_osk_notification_t::result_buffer member will be initialized to point + * to \a size bytes of storage, and that storage will be suitably aligned for + * storage of any structure. That is, the created buffer meets the same + * requirements as _mali_osk_malloc(). + * + * The notification object must be deleted when not in use. Use + * _mali_osk_notification_delete() for deleting it. + * + * @note You \b must \b not call _mali_osk_free() on a \ref _mali_osk_notification_t, + * object, or on a _mali_osk_notification_t::result_buffer. You must only use + * _mali_osk_notification_delete() to free the resources assocaited with a + * \ref _mali_osk_notification_t object. + * + * @param type The notification type + * @param size The size of the type specific buffer to send + * @return Pointer to a notification object with a suitable buffer, or NULL on error. + */ +_mali_osk_notification_t *_mali_osk_notification_create( u32 type, u32 size ); + +/** @brief Delete a notification object + * + * This must be called to reclaim the resources of a notification object. This + * includes: + * - The _mali_osk_notification_t::result_buffer + * - The \ref _mali_osk_notification_t itself. + * + * A notification object \b must \b not be used after it has been deleted by + * _mali_osk_notification_delete(). + * + * In addition, the notification object may not be deleted while it is in a + * queue. That is, if it has been placed on a queue with + * _mali_osk_notification_queue_send(), then it must not be deleted until + * it has been received by a call to _mali_osk_notification_queue_receive(). + * Otherwise, the queue may be corrupted. + * + * @param object the notification object to delete. + */ +void _mali_osk_notification_delete( _mali_osk_notification_t *object ); + +/** @brief Create a notification queue + * + * Creates a notification queue which can be used to queue messages for user + * delivery and get queued messages from + * + * The queue is a FIFO, and has no restrictions on the numbers of readers or + * writers. + * + * When the queue is no longer in use, it must be terminated with + * \ref _mali_osk_notification_queue_term(). Failure to do so will result in a + * memory leak. + * + * @return Pointer to a new notification queue or NULL on error. + */ +_mali_osk_notification_queue_t *_mali_osk_notification_queue_init( void ); + +/** @brief Destroy a notification queue + * + * Destroys a notification queue and frees associated resources from the queue. + * + * A notification queue \b must \b not be destroyed in the following cases: + * - while there are \ref _mali_osk_notification_t objects in the queue. + * - while there are writers currently acting upon the queue. That is, while + * a thread is currently calling \ref _mali_osk_notification_queue_send() on + * the queue, or while a thread may call + * \ref _mali_osk_notification_queue_send() on the queue in the future. + * - while there are readers currently waiting upon the queue. That is, while + * a thread is currently calling \ref _mali_osk_notification_queue_receive() on + * the queue, or while a thread may call + * \ref _mali_osk_notification_queue_receive() on the queue in the future. + * + * Therefore, all \ref _mali_osk_notification_t objects must be flushed and + * deleted by the code that makes use of the notification queues, since only + * they know the structure of the _mali_osk_notification_t::result_buffer + * (even if it may only be a flat sturcture). + * + * @note Since the queue is a FIFO, the code using notification queues may + * create its own 'flush' type of notification, to assist in flushing the + * queue. + * + * Once the queue has been destroyed, it must not be used again. + * + * @param queue The queue to destroy + */ +void _mali_osk_notification_queue_term( _mali_osk_notification_queue_t *queue ); + +/** @brief Schedule notification for delivery + * + * When a \ref _mali_osk_notification_t object has been created successfully + * and set up, it may be added to the queue of objects waiting for user space + * transfer. + * + * The sending will not block if the queue is full. + * + * A \ref _mali_osk_notification_t object \b must \b not be put on two different + * queues at the same time, or enqueued twice onto a single queue before + * reception. However, it is acceptable for it to be requeued \em after reception + * from a call to _mali_osk_notification_queue_receive(), even onto the same queue. + * + * Again, requeuing must also not enqueue onto two different queues at the same + * time, or enqueue onto the same queue twice before reception. + * + * @param queue The notification queue to add this notification to + * @param object The entry to add + */ +void _mali_osk_notification_queue_send( _mali_osk_notification_queue_t *queue, _mali_osk_notification_t *object ); + +/** @brief Receive a notification from a queue + * + * Receives a single notification from the given queue. + * + * If no notifciations are ready the thread will sleep until one becomes ready. + * Therefore, notifications may not be received into an + * IRQ or 'atomic' context (that is, a context where sleeping is disallowed). + * + * @param queue The queue to receive from + * @param result Pointer to storage of a pointer of type + * \ref _mali_osk_notification_t*. \a result will be written to such that the + * expression \a (*result) will evaluate to a pointer to a valid + * \ref _mali_osk_notification_t object, or NULL if none were received. + * @return _MALI_OSK_ERR_OK on success. _MALI_OSK_ERR_RESTARTSYSCALL if the sleep was interrupted. + */ +_mali_osk_errcode_t _mali_osk_notification_queue_receive( _mali_osk_notification_queue_t *queue, _mali_osk_notification_t **result ); + +/** @brief Dequeues a notification from a queue + * + * Receives a single notification from the given queue. + * + * If no notifciations are ready the function call will return an error code. + * + * @param queue The queue to receive from + * @param result Pointer to storage of a pointer of type + * \ref _mali_osk_notification_t*. \a result will be written to such that the + * expression \a (*result) will evaluate to a pointer to a valid + * \ref _mali_osk_notification_t object, or NULL if none were received. + * @return _MALI_OSK_ERR_OK on success, _MALI_OSK_ERR_ITEM_NOT_FOUND if queue was empty. + */ +_mali_osk_errcode_t _mali_osk_notification_queue_dequeue( _mali_osk_notification_queue_t *queue, _mali_osk_notification_t **result ); + +/** @} */ /* end group _mali_osk_notification */ + + +/** @addtogroup _mali_osk_timer + * + * Timers use the OS's representation of time, which are 'ticks'. This is to + * prevent aliasing problems between the internal timer time, and the time + * asked for. + * + * @{ */ + +/** @brief Initialize a timer + * + * Allocates resources for a new timer, and initializes them. This does not + * start the timer. + * + * @return a pointer to the allocated timer object, or NULL on failure. + */ +_mali_osk_timer_t *_mali_osk_timer_init(void); + +/** @brief Start a timer + * + * It is an error to start a timer without setting the callback via + * _mali_osk_timer_setcallback(). + * + * It is an error to use this to start an already started timer. + * + * The timer will expire in \a ticks_to_expire ticks, at which point, the + * callback function will be invoked with the callback-specific data, + * as registered by _mali_osk_timer_setcallback(). + * + * @param tim the timer to start + * @param ticks_to_expire the amount of time in ticks for the timer to run + * before triggering. + */ +void _mali_osk_timer_add( _mali_osk_timer_t *tim, u32 ticks_to_expire ); + +/** @brief Modify a timer + * + * Set the relative time at which a timer will expire, and start it if it is + * stopped. If \a ticks_to_expire 0 the timer fires immediately. + * + * It is an error to modify a timer without setting the callback via + * _mali_osk_timer_setcallback(). + * + * The timer will expire at \a ticks_to_expire from the time of the call, at + * which point, the callback function will be invoked with the + * callback-specific data, as set by _mali_osk_timer_setcallback(). + * + * @param tim the timer to modify, and start if necessary + * @param ticks_to_expire the \em absolute time in ticks at which this timer + * should trigger. + * + */ +void _mali_osk_timer_mod( _mali_osk_timer_t *tim, u32 ticks_to_expire); + +/** @brief Stop a timer, and block on its completion. + * + * Stop the timer. When the function returns, it is guaranteed that the timer's + * callback will not be running on any CPU core. + * + * Since stoping the timer blocks on compeletion of the callback, the callback + * may not obtain any mutexes that the caller holds. Otherwise, a deadlock will + * occur. + * + * @note While the callback itself is guaranteed to not be running, work + * enqueued on the work-queue by the timer (with + * \ref _mali_osk_wq_schedule_work()) may still run. The timer callback and + * work handler must take this into account. + * + * It is legal to stop an already stopped timer. + * + * @param tim the timer to stop. + * + */ +void _mali_osk_timer_del( _mali_osk_timer_t *tim ); + +/** @brief Stop a timer. + * + * Stop the timer. When the function returns, the timer's callback may still be + * running on any CPU core. + * + * It is legal to stop an already stopped timer. + * + * @param tim the timer to stop. + */ +void _mali_osk_timer_del_async( _mali_osk_timer_t *tim ); + +/** @brief Check if timer is pending. + * + * Check if timer is active. + * + * @param tim the timer to check + * @return MALI_TRUE if time is active, MALI_FALSE if it is not active + */ +mali_bool _mali_osk_timer_pending( _mali_osk_timer_t *tim); + +/** @brief Set a timer's callback parameters. + * + * This must be called at least once before a timer is started/modified. + * + * After a timer has been stopped or expires, the callback remains set. This + * means that restarting the timer will call the same function with the same + * parameters on expiry. + * + * @param tim the timer to set callback on. + * @param callback Function to call when timer expires + * @param data Function-specific data to supply to the function on expiry. + */ +void _mali_osk_timer_setcallback( _mali_osk_timer_t *tim, _mali_osk_timer_callback_t callback, void *data ); + +/** @brief Terminate a timer, and deallocate resources. + * + * The timer must first be stopped by calling _mali_osk_timer_del(). + * + * It is a programming error for _mali_osk_timer_term() to be called on: + * - timer that is currently running + * - a timer that is currently executing its callback. + * + * @param tim the timer to deallocate. + */ +void _mali_osk_timer_term( _mali_osk_timer_t *tim ); +/** @} */ /* end group _mali_osk_timer */ + + +/** @defgroup _mali_osk_time OSK Time functions + * + * \ref _mali_osk_time use the OS's representation of time, which are + * 'ticks'. This is to prevent aliasing problems between the internal timer + * time, and the time asked for. + * + * OS tick time is measured as a u32. The time stored in a u32 may either be + * an absolute time, or a time delta between two events. Whilst it is valid to + * use math opeartors to \em change the tick value represented as a u32, it + * is often only meaningful to do such operations on time deltas, rather than + * on absolute time. However, it is meaningful to add/subtract time deltas to + * absolute times. + * + * Conversion between tick time and milliseconds (ms) may not be loss-less, + * and are \em implementation \em depenedant. + * + * Code use OS time must take this into account, since: + * - a small OS time may (or may not) be rounded + * - a large time may (or may not) overflow + * + * @{ */ + +/** @brief Return whether ticka occurs after tickb + * + * Some OSs handle tick 'rollover' specially, and so can be more robust against + * tick counters rolling-over. This function must therefore be called to + * determine if a time (in ticks) really occurs after another time (in ticks). + * + * @param ticka ticka + * @param tickb tickb + * @return non-zero if ticka represents a time that occurs after tickb. + * Zero otherwise. + */ +int _mali_osk_time_after( u32 ticka, u32 tickb ); + +/** @brief Convert milliseconds to OS 'ticks' + * + * @param ms time interval in milliseconds + * @return the corresponding time interval in OS ticks. + */ +u32 _mali_osk_time_mstoticks( u32 ms ); + +/** @brief Convert OS 'ticks' to milliseconds + * + * @param ticks time interval in OS ticks. + * @return the corresponding time interval in milliseconds + */ +u32 _mali_osk_time_tickstoms( u32 ticks ); + + +/** @brief Get the current time in OS 'ticks'. + * @return the current time in OS 'ticks'. + */ +u32 _mali_osk_time_tickcount( void ); + +/** @brief Cause a microsecond delay + * + * The delay will have microsecond resolution, and is necessary for correct + * operation of the driver. At worst, the delay will be \b at least \a usecs + * microseconds, and so may be (significantly) more. + * + * This function may be implemented as a busy-wait, which is the most sensible + * implementation. On OSs where there are situations in which a thread must not + * sleep, this is definitely implemented as a busy-wait. + * + * @param usecs the number of microseconds to wait for. + */ +void _mali_osk_time_ubusydelay( u32 usecs ); + +/** @brief Return time in nano seconds, since any given reference. + * + * @return Time in nano seconds + */ +u64 _mali_osk_time_get_ns( void ); + + +/** @} */ /* end group _mali_osk_time */ + +/** @defgroup _mali_osk_math OSK Math + * @{ */ + +/** @brief Count Leading Zeros (Little-endian) + * + * @note This function must be implemented to support the reference + * implementation of _mali_osk_find_first_zero_bit, as defined in + * mali_osk_bitops.h. + * + * @param val 32-bit words to count leading zeros on + * @return the number of leading zeros. + */ +u32 _mali_osk_clz( u32 val ); + +/** @brief find last (most-significant) bit set + * + * @param val 32-bit words to count last bit set on + * @return last bit set. + */ +u32 _mali_osk_fls( u32 val ); + +/** @} */ /* end group _mali_osk_math */ + +/** @addtogroup _mali_osk_wait_queue OSK Wait Queue functionality + * @{ */ + +/** @brief Initialize an empty Wait Queue */ +_mali_osk_wait_queue_t* _mali_osk_wait_queue_init( void ); + +/** @brief Sleep if condition is false + * + * @param queue the queue to use + * @param condition function pointer to a boolean function + * @param data data parameter for condition function + * + * Put thread to sleep if the given \a condition function returns false. When + * being asked to wake up again, the condition will be re-checked and the + * thread only woken up if the condition is now true. + */ +void _mali_osk_wait_queue_wait_event( _mali_osk_wait_queue_t *queue, mali_bool (*condition)(void *), void *data ); + +/** @brief Sleep if condition is false + * + * @param queue the queue to use + * @param condition function pointer to a boolean function + * @param data data parameter for condition function + * @param timeout timeout in ms + * + * Put thread to sleep if the given \a condition function returns false. When + * being asked to wake up again, the condition will be re-checked and the + * thread only woken up if the condition is now true. Will return if time + * exceeds timeout. + */ +void _mali_osk_wait_queue_wait_event_timeout( _mali_osk_wait_queue_t *queue, mali_bool (*condition)(void *), void *data, u32 timeout ); + +/** @brief Wake up all threads in wait queue if their respective conditions are + * true + * + * @param queue the queue whose threads should be woken up + * + * Wake up all threads in wait queue \a queue whose condition is now true. + */ +void _mali_osk_wait_queue_wake_up( _mali_osk_wait_queue_t *queue ); + +/** @brief terminate a wait queue + * + * @param queue the queue to terminate. + */ +void _mali_osk_wait_queue_term( _mali_osk_wait_queue_t *queue ); +/** @} */ /* end group _mali_osk_wait_queue */ + + +/** @addtogroup _mali_osk_miscellaneous + * @{ */ + +/** @brief Output a device driver debug message. + * + * The interpretation of \a fmt is the same as the \c format parameter in + * _mali_osu_vsnprintf(). + * + * @param fmt a _mali_osu_vsnprintf() style format string + * @param ... a variable-number of parameters suitable for \a fmt + */ +void _mali_osk_dbgmsg( const char *fmt, ... ); + +/** @brief Print fmt into buf. + * + * The interpretation of \a fmt is the same as the \c format parameter in + * _mali_osu_vsnprintf(). + * + * @param buf a pointer to the result buffer + * @param size the total number of bytes allowed to write to \a buf + * @param fmt a _mali_osu_vsnprintf() style format string + * @param ... a variable-number of parameters suitable for \a fmt + * @return The number of bytes written to \a buf + */ +u32 _mali_osk_snprintf( char *buf, u32 size, const char *fmt, ... ); + +/** @brief Abnormal process abort. + * + * Terminates the caller-process if this function is called. + * + * This function will be called from Debug assert-macros in mali_kernel_common.h. + * + * This function will never return - because to continue from a Debug assert + * could cause even more problems, and hinder debugging of the initial problem. + * + * This function is only used in Debug builds, and is not used in Release builds. + */ +void _mali_osk_abort(void); + +/** @brief Sets breakpoint at point where function is called. + * + * This function will be called from Debug assert-macros in mali_kernel_common.h, + * to assist in debugging. If debugging at this level is not required, then this + * function may be implemented as a stub. + * + * This function is only used in Debug builds, and is not used in Release builds. + */ +void _mali_osk_break(void); + +/** @brief Return an identificator for calling process. + * + * @return Identificator for calling process. + */ +u32 _mali_osk_get_pid(void); + +/** @brief Return an identificator for calling thread. + * + * @return Identificator for calling thread. + */ +u32 _mali_osk_get_tid(void); + +/** @brief Enable OS controlled runtime power management + */ +void _mali_osk_pm_dev_enable(void); + +/** @brief Disable OS controlled runtime power management + */ +void _mali_osk_pm_dev_disable(void); + + +/** @brief Take a reference to the power manager system for the Mali device. + * + * When function returns successfully, Mali is ON. + * + * @note Call \a _mali_osk_pm_dev_ref_dec() to release this reference. + */ +_mali_osk_errcode_t _mali_osk_pm_dev_ref_add(void); + + +/** @brief Release the reference to the power manger system for the Mali device. + * + * When reference count reach zero, the cores can be off. + * + * @note This must be used to release references taken with \a _mali_osk_pm_dev_ref_add(). + */ +void _mali_osk_pm_dev_ref_dec(void); + + +/** @brief Take a reference to the power manager system for the Mali device. + * + * Will leave the cores powered off if they are already powered off. + * + * @note Call \a _mali_osk_pm_dev_ref_dec() to release this reference. + * + * @return MALI_TRUE if the Mali GPU is powered on, otherwise MALI_FALSE. + */ +mali_bool _mali_osk_pm_dev_ref_add_no_power_on(void); + + +/** @brief Releasing the reference to the power manger system for the Mali device. + * + * When reference count reach zero, the cores can be off. + * + * @note This must be used to release references taken with \a _mali_osk_pm_dev_ref_add_no_power_on(). + */ +void _mali_osk_pm_dev_ref_dec_no_power_on(void); + +/** @brief Block untill pending PM operations are done + */ +void _mali_osk_pm_dev_barrier(void); + +/** @} */ /* end group _mali_osk_miscellaneous */ + +/** @} */ /* end group osuapi */ + +/** @} */ /* end group uddapi */ + + + +#ifdef __cplusplus +} +#endif + +/* Check standard inlines */ +#ifndef MALI_STATIC_INLINE +#error MALI_STATIC_INLINE not defined on your OS +#endif + +#ifndef MALI_NON_STATIC_INLINE +#error MALI_NON_STATIC_INLINE not defined on your OS +#endif + +#endif /* __MALI_OSK_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_osk_bitops.h b/drivers/gpu/arm/mali/common/mali_osk_bitops.h new file mode 100644 index 00000000000000..35f2e67050f933 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_osk_bitops.h @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2010, 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_bitops.h + * Implementation of the OS abstraction layer for the kernel device driver + */ + +#ifndef __MALI_OSK_BITOPS_H__ +#define __MALI_OSK_BITOPS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +MALI_STATIC_INLINE void _mali_internal_clear_bit( u32 bit, u32 *addr ) +{ + MALI_DEBUG_ASSERT( bit < 32 ); + MALI_DEBUG_ASSERT( NULL != addr ); + + (*addr) &= ~(1 << bit); +} + +MALI_STATIC_INLINE void _mali_internal_set_bit( u32 bit, u32 *addr ) +{ + MALI_DEBUG_ASSERT( bit < 32 ); + MALI_DEBUG_ASSERT( NULL != addr ); + + (*addr) |= (1 << bit); +} + +MALI_STATIC_INLINE u32 _mali_internal_test_bit( u32 bit, u32 value ) +{ + MALI_DEBUG_ASSERT( bit < 32 ); + return value & (1 << bit); +} + +MALI_STATIC_INLINE int _mali_internal_find_first_zero_bit( u32 value ) +{ + u32 inverted; + u32 negated; + u32 isolated; + u32 leading_zeros; + + /* Begin with xxx...x0yyy...y, where ys are 1, number of ys is in range 0..31 */ + inverted = ~value; /* zzz...z1000...0 */ + /* Using count_trailing_zeros on inverted value - + * See ARM System Developers Guide for details of count_trailing_zeros */ + + /* Isolate the zero: it is preceeded by a run of 1s, so add 1 to it */ + negated = (u32)-inverted ; /* -a == ~a + 1 (mod 2^n) for n-bit numbers */ + /* negated = xxx...x1000...0 */ + + isolated = negated & inverted ; /* xxx...x1000...0 & zzz...z1000...0, zs are ~xs */ + /* And so the first zero bit is in the same position as the 1 == number of 1s that preceeded it + * Note that the output is zero if value was all 1s */ + + leading_zeros = _mali_osk_clz( isolated ); + + return 31 - leading_zeros; +} + + +/** @defgroup _mali_osk_bitops OSK Non-atomic Bit-operations + * @{ */ + +/** + * These bit-operations do not work atomically, and so locks must be used if + * atomicity is required. + * + * Reference implementations for Little Endian are provided, and so it should + * not normally be necessary to re-implement these. Efficient bit-twiddling + * techniques are used where possible, implemented in portable C. + * + * Note that these reference implementations rely on _mali_osk_clz() being + * implemented. + */ + +/** @brief Clear a bit in a sequence of 32-bit words + * @param nr bit number to clear, starting from the (Little-endian) least + * significant bit + * @param addr starting point for counting. + */ +MALI_STATIC_INLINE void _mali_osk_clear_nonatomic_bit( u32 nr, u32 *addr ) +{ + addr += nr >> 5; /* find the correct word */ + nr = nr & ((1 << 5)-1); /* The bit number within the word */ + + _mali_internal_clear_bit( nr, addr ); +} + +/** @brief Set a bit in a sequence of 32-bit words + * @param nr bit number to set, starting from the (Little-endian) least + * significant bit + * @param addr starting point for counting. + */ +MALI_STATIC_INLINE void _mali_osk_set_nonatomic_bit( u32 nr, u32 *addr ) +{ + addr += nr >> 5; /* find the correct word */ + nr = nr & ((1 << 5)-1); /* The bit number within the word */ + + _mali_internal_set_bit( nr, addr ); +} + +/** @brief Test a bit in a sequence of 32-bit words + * @param nr bit number to test, starting from the (Little-endian) least + * significant bit + * @param addr starting point for counting. + * @return zero if bit was clear, non-zero if set. Do not rely on the return + * value being related to the actual word under test. + */ +MALI_STATIC_INLINE u32 _mali_osk_test_bit( u32 nr, u32 *addr ) +{ + addr += nr >> 5; /* find the correct word */ + nr = nr & ((1 << 5)-1); /* The bit number within the word */ + + return _mali_internal_test_bit( nr, *addr ); +} + +/* Return maxbit if not found */ +/** @brief Find the first zero bit in a sequence of 32-bit words + * @param addr starting point for search. + * @param maxbit the maximum number of bits to search + * @return the number of the first zero bit found, or maxbit if none were found + * in the specified range. + */ +MALI_STATIC_INLINE u32 _mali_osk_find_first_zero_bit( const u32 *addr, u32 maxbit ) +{ + u32 total; + + for ( total = 0; total < maxbit; total += 32, ++addr ) { + int result; + result = _mali_internal_find_first_zero_bit( *addr ); + + /* non-negative signifies the bit was found */ + if ( result >= 0 ) { + total += (u32)result; + break; + } + } + + /* Now check if we reached maxbit or above */ + if ( total >= maxbit ) { + total = maxbit; + } + + return total; /* either the found bit nr, or maxbit if not found */ +} +/** @} */ /* end group _mali_osk_bitops */ + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_OSK_BITOPS_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_osk_list.h b/drivers/gpu/arm/mali/common/mali_osk_list.h new file mode 100644 index 00000000000000..65a1d50b6dc7db --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_osk_list.h @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_list.h + * Implementation of the OS abstraction layer for the kernel device driver + */ + +#ifndef __MALI_OSK_LIST_H__ +#define __MALI_OSK_LIST_H__ + +#include "mali_osk.h" +#include "mali_kernel_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +MALI_STATIC_INLINE void __mali_osk_list_add(_mali_osk_list_t *new_entry, _mali_osk_list_t *prev, _mali_osk_list_t *next) +{ + next->prev = new_entry; + new_entry->next = next; + new_entry->prev = prev; + prev->next = new_entry; +} + +MALI_STATIC_INLINE void __mali_osk_list_del(_mali_osk_list_t *prev, _mali_osk_list_t *next) +{ + next->prev = prev; + prev->next = next; +} + +/** @addtogroup _mali_osk_list OSK Doubly-Linked Circular Lists + * @{ */ + +/** Reference implementations of Doubly-linked Circular Lists are provided. + * There is often no need to re-implement these. + * + * @note The implementation may differ subtly from any lists the OS provides. + * For this reason, these lists should not be mixed with OS-specific lists + * inside the OSK/UKK implementation. */ + +/** @brief Initialize a list to be a head of an empty list + * @param exp the list to initialize. */ +#define _MALI_OSK_INIT_LIST_HEAD(exp) _mali_osk_list_init(exp) + +/** @brief Define a list variable, which is uninitialized. + * @param exp the name of the variable that the list will be defined as. */ +#define _MALI_OSK_LIST_HEAD(exp) _mali_osk_list_t exp + +/** @brief Define a list variable, which is initialized. + * @param exp the name of the variable that the list will be defined as. */ +#define _MALI_OSK_LIST_HEAD_STATIC_INIT(exp) _mali_osk_list_t exp = { &exp, &exp } + +/** @brief Initialize a list element. + * + * All list elements must be initialized before use. + * + * Do not use on any list element that is present in a list without using + * _mali_osk_list_del first, otherwise this will break the list. + * + * @param list the list element to initialize + */ +MALI_STATIC_INLINE void _mali_osk_list_init( _mali_osk_list_t *list ) +{ + list->next = list; + list->prev = list; +} + +/** @brief Insert a single list element after an entry in a list + * + * As an example, if this is inserted to the head of a list, then this becomes + * the first element of the list. + * + * Do not use to move list elements from one list to another, as it will break + * the originating list. + * + * + * @param newlist the list element to insert + * @param list the list in which to insert. The new element will be the next + * entry in this list + */ +MALI_STATIC_INLINE void _mali_osk_list_add( _mali_osk_list_t *new_entry, _mali_osk_list_t *list ) +{ + __mali_osk_list_add(new_entry, list, list->next); +} + +/** @brief Insert a single list element before an entry in a list + * + * As an example, if this is inserted to the head of a list, then this becomes + * the last element of the list. + * + * Do not use to move list elements from one list to another, as it will break + * the originating list. + * + * @param newlist the list element to insert + * @param list the list in which to insert. The new element will be the previous + * entry in this list + */ +MALI_STATIC_INLINE void _mali_osk_list_addtail( _mali_osk_list_t *new_entry, _mali_osk_list_t *list ) +{ + __mali_osk_list_add(new_entry, list->prev, list); +} + +/** @brief Remove a single element from a list + * + * The element will no longer be present in the list. The removed list element + * will be uninitialized, and so should not be traversed. It must be + * initialized before further use. + * + * @param list the list element to remove. + */ +MALI_STATIC_INLINE void _mali_osk_list_del( _mali_osk_list_t *list ) +{ + __mali_osk_list_del(list->prev, list->next); +} + +/** @brief Remove a single element from a list, and re-initialize it + * + * The element will no longer be present in the list. The removed list element + * will initialized, and so can be used as normal. + * + * @param list the list element to remove and initialize. + */ +MALI_STATIC_INLINE void _mali_osk_list_delinit( _mali_osk_list_t *list ) +{ + __mali_osk_list_del(list->prev, list->next); + _mali_osk_list_init(list); +} + +/** @brief Determine whether a list is empty. + * + * An empty list is one that contains a single element that points to itself. + * + * @param list the list to check. + * @return non-zero if the list is empty, and zero otherwise. + */ +MALI_STATIC_INLINE mali_bool _mali_osk_list_empty( _mali_osk_list_t *list ) +{ + return list->next == list; +} + +/** @brief Move a list element from one list to another. + * + * The list element must be initialized. + * + * As an example, moving a list item to the head of a new list causes this item + * to be the first element in the new list. + * + * @param move the list element to move + * @param list the new list into which the element will be inserted, as the next + * element in the list. + */ +MALI_STATIC_INLINE void _mali_osk_list_move( _mali_osk_list_t *move_entry, _mali_osk_list_t *list ) +{ + __mali_osk_list_del(move_entry->prev, move_entry->next); + _mali_osk_list_add(move_entry, list); +} + +/** @brief Move an entire list + * + * The list element must be initialized. + * + * Allows you to move a list from one list head to another list head + * + * @param old_list The existing list head + * @param new_list The new list head (must be an empty list) + */ +MALI_STATIC_INLINE void _mali_osk_list_move_list( _mali_osk_list_t *old_list, _mali_osk_list_t *new_list ) +{ + MALI_DEBUG_ASSERT(_mali_osk_list_empty(new_list)); + if (!_mali_osk_list_empty(old_list)) { + new_list->next = old_list->next; + new_list->prev = old_list->prev; + new_list->next->prev = new_list; + new_list->prev->next = new_list; + old_list->next = old_list; + old_list->prev = old_list; + } +} + +/** @brief Find the containing structure of a list + * + * When traversing a list, this is used to recover the containing structure, + * given that is contains a _mali_osk_list_t member. + * + * Each list must be of structures of one type, and must link the same members + * together, otherwise it will not be possible to correctly recover the + * sturctures that the lists link. + * + * @note no type or memory checking occurs to ensure that a structure does in + * fact exist for the list entry, and that it is being recovered with respect + * to the correct list member. + * + * @param ptr the pointer to the _mali_osk_list_t member in this structure + * @param type the type of the structure that contains the member + * @param member the member of the structure that ptr points to. + * @return a pointer to a \a type object which contains the _mali_osk_list_t + * \a member, as pointed to by the _mali_osk_list_t \a *ptr. + */ +#define _MALI_OSK_LIST_ENTRY(ptr, type, member) \ + _MALI_OSK_CONTAINER_OF(ptr, type, member) + +/** @brief Enumerate a list safely + * + * With this macro, lists can be enumerated in a 'safe' manner. That is, + * entries can be deleted from the list without causing an error during + * enumeration. To achieve this, a 'temporary' pointer is required, which must + * be provided to the macro. + * + * Use it like a 'for()', 'while()' or 'do()' construct, and so it must be + * followed by a statement or compound-statement which will be executed for + * each list entry. + * + * Upon loop completion, providing that an early out was not taken in the + * loop body, then it is guaranteed that ptr->member == list, even if the loop + * body never executed. + * + * @param ptr a pointer to an object of type 'type', which points to the + * structure that contains the currently enumerated list entry. + * @param tmp a pointer to an object of type 'type', which must not be used + * inside the list-execution statement. + * @param list a pointer to a _mali_osk_list_t, from which enumeration will + * begin + * @param type the type of the structure that contains the _mali_osk_list_t + * member that is part of the list to be enumerated. + * @param member the _mali_osk_list_t member of the structure that is part of + * the list to be enumerated. + */ +#define _MALI_OSK_LIST_FOREACHENTRY(ptr, tmp, list, type, member) \ + for (ptr = _MALI_OSK_LIST_ENTRY((list)->next, type, member), \ + tmp = _MALI_OSK_LIST_ENTRY(ptr->member.next, type, member); \ + &ptr->member != (list); \ + ptr = tmp, \ + tmp = _MALI_OSK_LIST_ENTRY(tmp->member.next, type, member)) + +/** @brief Enumerate a list in reverse order safely + * + * This macro is identical to @ref _MALI_OSK_LIST_FOREACHENTRY, except that + * entries are enumerated in reverse order. + * + * @param ptr a pointer to an object of type 'type', which points to the + * structure that contains the currently enumerated list entry. + * @param tmp a pointer to an object of type 'type', which must not be used + * inside the list-execution statement. + * @param list a pointer to a _mali_osk_list_t, from which enumeration will + * begin + * @param type the type of the structure that contains the _mali_osk_list_t + * member that is part of the list to be enumerated. + * @param member the _mali_osk_list_t member of the structure that is part of + * the list to be enumerated. + */ +#define _MALI_OSK_LIST_FOREACHENTRY_REVERSE(ptr, tmp, list, type, member) \ + for (ptr = _MALI_OSK_LIST_ENTRY((list)->prev, type, member), \ + tmp = _MALI_OSK_LIST_ENTRY(ptr->member.prev, type, member); \ + &ptr->member != (list); \ + ptr = tmp, \ + tmp = _MALI_OSK_LIST_ENTRY(tmp->member.prev, type, member)) + +/** @} */ /* end group _mali_osk_list */ + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_OSK_LIST_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_osk_mali.h b/drivers/gpu/arm/mali/common/mali_osk_mali.h new file mode 100644 index 00000000000000..7ed701cf77a704 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_osk_mali.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_mali.h + * Defines the OS abstraction layer which is specific for the Mali kernel device driver (OSK) + */ + +#ifndef __MALI_OSK_MALI_H__ +#define __MALI_OSK_MALI_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @addtogroup _mali_osk_miscellaneous + * @{ */ + +/** @brief Struct with device specific configuration data + */ +struct _mali_osk_device_data { + /* Dedicated GPU memory range (physical). */ + u32 dedicated_mem_start; + u32 dedicated_mem_size; + + /* Shared GPU memory */ + u32 shared_mem_size; + + /* Frame buffer memory to be accessible by Mali GPU (physical) */ + u32 fb_start; + u32 fb_size; + + /* Max runtime [ms] for jobs */ + int max_job_runtime; + + /* Report GPU utilization in this interval (specified in ms) */ + u32 utilization_interval; + + /* Function that will receive periodic GPU utilization numbers */ + void (*utilization_callback)(struct mali_gpu_utilization_data *data); + + /* + * Mali PMU switch delay. + * Only needed if the power gates are connected to the PMU in a high fanout + * network. This value is the number of Mali clock cycles it takes to + * enable the power gates and turn on the power mesh. + * This value will have no effect if a daisy chain implementation is used. + */ + u32 pmu_switch_delay; + + /* Mali Dynamic power domain configuration in sequence from 0-11 + * GP PP0 PP1 PP2 PP3 PP4 PP5 PP6 PP7, L2$0 L2$1 L2$2 + */ + u16 pmu_domain_config[12]; + + /* Fuction that platform callback for freq tunning, needed when MALI400_POWER_PERFORMANCE_POLICY enabled */ + int (*set_freq_callback)(unsigned int mhz); +}; + +/** @brief Find Mali GPU HW resource + * + * @param addr Address of Mali GPU resource to find + * @param res Storage for resource information if resource is found. + * @return _MALI_OSK_ERR_OK on success, _MALI_OSK_ERR_ITEM_NOT_FOUND if resource is not found + */ +_mali_osk_errcode_t _mali_osk_resource_find(u32 addr, _mali_osk_resource_t *res); + + +/** @brief Find Mali GPU HW base address + * + * @return 0 if resources are found, otherwise the Mali GPU component with lowest address. + */ +u32 _mali_osk_resource_base_address(void); + +/** @brief Retrieve the Mali GPU specific data + * + * @return _MALI_OSK_ERR_OK on success, otherwise failure. + */ +_mali_osk_errcode_t _mali_osk_device_data_get(struct _mali_osk_device_data *data); + +/** @brief Determines if Mali GPU has been configured with shared interrupts. + * + * @return MALI_TRUE if shared interrupts, MALI_FALSE if not. + */ +mali_bool _mali_osk_shared_interrupts(void); + +/** @} */ /* end group _mali_osk_miscellaneous */ + +/** @addtogroup _mali_osk_low_level_memory + * @{ */ + +/** @brief Copy as much data as possible from src to dest, do not crash if src or dest isn't available. + * + * @param dest Destination buffer (limited to user space mapped Mali memory) + * @param src Source buffer + * @param size Number of bytes to copy + * @return Number of bytes actually copied + */ +u32 _mali_osk_mem_write_safe(void *dest, const void *src, u32 size); + +/** @} */ /* end group _mali_osk_low_level_memory */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_OSK_MALI_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_osk_profiling.h b/drivers/gpu/arm/mali/common/mali_osk_profiling.h new file mode 100644 index 00000000000000..520cd5e96482e4 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_osk_profiling.h @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_OSK_PROFILING_H__ +#define __MALI_OSK_PROFILING_H__ + +#if defined(CONFIG_MALI400_PROFILING) && defined (CONFIG_TRACEPOINTS) + +#include "mali_linux_trace.h" +#include "mali_profiling_events.h" +#include "mali_profiling_gator_api.h" + +#define MALI_PROFILING_MAX_BUFFER_ENTRIES 1048576 + +#define MALI_PROFILING_NO_HW_COUNTER = ((u32)-1) + +/** @defgroup _mali_osk_profiling External profiling connectivity + * @{ */ + +/** + * Initialize the profiling module. + * @return _MALI_OSK_ERR_OK on success, otherwise failure. + */ +_mali_osk_errcode_t _mali_osk_profiling_init(mali_bool auto_start); + +/* + * Terminate the profiling module. + */ +void _mali_osk_profiling_term(void); + +/** + * Start recording profiling data + * + * The specified limit will determine how large the capture buffer is. + * MALI_PROFILING_MAX_BUFFER_ENTRIES determines the maximum size allowed by the device driver. + * + * @param limit The desired maximum number of events to record on input, the actual maximum on output. + * @return _MALI_OSK_ERR_OK on success, otherwise failure. + */ +_mali_osk_errcode_t _mali_osk_profiling_start(u32 * limit); + +/** + * Add an profiling event + * + * @param event_id The event identificator. + * @param data0 First data parameter, depending on event_id specified. + * @param data1 Second data parameter, depending on event_id specified. + * @param data2 Third data parameter, depending on event_id specified. + * @param data3 Fourth data parameter, depending on event_id specified. + * @param data4 Fifth data parameter, depending on event_id specified. + * @return _MALI_OSK_ERR_OK on success, otherwise failure. + */ +/* Call Linux tracepoint directly */ +#define _mali_osk_profiling_add_event(event_id, data0, data1, data2, data3, data4) trace_mali_timeline_event((event_id), (data0), (data1), (data2), (data3), (data4)) + +/** + * Report a hardware counter event. + * + * @param counter_id The ID of the counter. + * @param value The value of the counter. + */ + +/* Call Linux tracepoint directly */ +#define _mali_osk_profiling_report_hw_counter(counter_id, value) trace_mali_hw_counter(counter_id, value) + +/** + * Report SW counters + * + * @param counters array of counter values + */ +void _mali_osk_profiling_report_sw_counters(u32 *counters); + +/** + * Stop recording profiling data + * + * @param count Returns the number of recorded events. + * @return _MALI_OSK_ERR_OK on success, otherwise failure. + */ +_mali_osk_errcode_t _mali_osk_profiling_stop(u32 * count); + +/** + * Retrieves the number of events that can be retrieved + * + * @return The number of recorded events that can be retrieved. + */ +u32 _mali_osk_profiling_get_count(void); + +/** + * Retrieve an event + * + * @param index Event index (start with 0 and continue until this function fails to retrieve all events) + * @param timestamp The timestamp for the retrieved event will be stored here. + * @param event_id The event ID for the retrieved event will be stored here. + * @param data The 5 data values for the retrieved event will be stored here. + * @return _MALI_OSK_ERR_OK on success, otherwise failure. + */ +_mali_osk_errcode_t _mali_osk_profiling_get_event(u32 index, u64* timestamp, u32* event_id, u32 data[5]); + +/** + * Clear the recorded buffer. + * + * This is needed in order to start another recording. + * + * @return _MALI_OSK_ERR_OK on success, otherwise failure. + */ +_mali_osk_errcode_t _mali_osk_profiling_clear(void); + +/** + * Checks if a recording of profiling data is in progress + * + * @return MALI_TRUE if recording of profiling data is in progress, MALI_FALSE if not + */ +mali_bool _mali_osk_profiling_is_recording(void); + +/** + * Checks if profiling data is available for retrival + * + * @return MALI_TRUE if profiling data is avaiable, MALI_FALSE if not + */ +mali_bool _mali_osk_profiling_have_recording(void); + +/** @} */ /* end group _mali_osk_profiling */ + +#else /* defined(CONFIG_MALI400_PROFILING) && defined(CONFIG_TRACEPOINTS) */ + +/* Dummy add_event, for when profiling is disabled. */ + +#define _mali_osk_profiling_add_event(event_id, data0, data1, data2, data3, data4) + +#endif /* defined(CONFIG_MALI400_PROFILING) && defined(CONFIG_TRACEPOINTS) */ + +#endif /* __MALI_OSK_PROFILING_H__ */ + + diff --git a/drivers/gpu/arm/mali/common/mali_osk_types.h b/drivers/gpu/arm/mali/common/mali_osk_types.h new file mode 100644 index 00000000000000..6d64cd91fce473 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_osk_types.h @@ -0,0 +1,455 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_types.h + * Defines types of the OS abstraction layer for the kernel device driver (OSK) + */ + +#ifndef __MALI_OSK_TYPES_H__ +#define __MALI_OSK_TYPES_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup uddapi Unified Device Driver (UDD) APIs + * + * @{ + */ + +/** + * @addtogroup oskapi UDD OS Abstraction for Kernel-side (OSK) APIs + * + * @{ + */ + +/** @defgroup _mali_osk_miscellaneous OSK Miscellaneous functions, constants and types + * @{ */ + +/* Define integer types used by OSK. Note: these currently clash with Linux so we only define them if not defined already */ +#ifndef __KERNEL__ +typedef unsigned char u8; +typedef signed char s8; +typedef unsigned short u16; +typedef signed short s16; +typedef unsigned int u32; +typedef signed int s32; +typedef unsigned long long u64; +#define BITS_PER_LONG (sizeof(long)*8) +#else +/* Ensure Linux types u32, etc. are defined */ +#include +#endif + +/** @brief Mali Boolean type which uses MALI_TRUE and MALI_FALSE + */ +typedef unsigned long mali_bool; + +#ifndef MALI_TRUE +#define MALI_TRUE ((mali_bool)1) +#endif + +#ifndef MALI_FALSE +#define MALI_FALSE ((mali_bool)0) +#endif + +#define MALI_HW_CORE_NO_COUNTER ((u32)-1) + +/** + * @brief OSK Error codes + * + * Each OS may use its own set of error codes, and may require that the + * User/Kernel interface take certain error code. This means that the common + * error codes need to be sufficiently rich to pass the correct error code + * thorugh from the OSK to U/K layer, across all OSs. + * + * The result is that some error codes will appear redundant on some OSs. + * Under all OSs, the OSK layer must translate native OS error codes to + * _mali_osk_errcode_t codes. Similarly, the U/K layer must translate from + * _mali_osk_errcode_t codes to native OS error codes. + */ +typedef enum { + _MALI_OSK_ERR_OK = 0, /**< Success. */ + _MALI_OSK_ERR_FAULT = -1, /**< General non-success */ + _MALI_OSK_ERR_INVALID_FUNC = -2, /**< Invalid function requested through User/Kernel interface (e.g. bad IOCTL number) */ + _MALI_OSK_ERR_INVALID_ARGS = -3, /**< Invalid arguments passed through User/Kernel interface */ + _MALI_OSK_ERR_NOMEM = -4, /**< Insufficient memory */ + _MALI_OSK_ERR_TIMEOUT = -5, /**< Timeout occurred */ + _MALI_OSK_ERR_RESTARTSYSCALL = -6, /**< Special: On certain OSs, must report when an interruptable mutex is interrupted. Ignore otherwise. */ + _MALI_OSK_ERR_ITEM_NOT_FOUND = -7, /**< Table Lookup failed */ + _MALI_OSK_ERR_BUSY = -8, /**< Device/operation is busy. Try again later */ + _MALI_OSK_ERR_UNSUPPORTED = -9, /**< Optional part of the interface used, and is unsupported */ +} _mali_osk_errcode_t; + +/** @} */ /* end group _mali_osk_miscellaneous */ + +/** @defgroup _mali_osk_wq OSK work queues + * @{ */ + +/** @brief Private type for work objects */ +typedef struct _mali_osk_wq_work_s _mali_osk_wq_work_t; +typedef struct _mali_osk_wq_delayed_work_s _mali_osk_wq_delayed_work_t; + +/** @brief Work queue handler function + * + * This function type is called when the work is scheduled by the work queue, + * e.g. as an IRQ bottom-half handler. + * + * Refer to \ref _mali_osk_wq_schedule_work() for more information on the + * work-queue and work handlers. + * + * @param arg resource-specific data + */ +typedef void (*_mali_osk_wq_work_handler_t)( void * arg ); + +/* @} */ /* end group _mali_osk_wq */ + +/** @defgroup _mali_osk_irq OSK IRQ handling + * @{ */ + +/** @brief Private type for IRQ handling objects */ +typedef struct _mali_osk_irq_t_struct _mali_osk_irq_t; + +/** @brief Optional function to trigger an irq from a resource + * + * This function is implemented by the common layer to allow probing of a resource's IRQ. + * @param arg resource-specific data */ +typedef void (*_mali_osk_irq_trigger_t)( void * arg ); + +/** @brief Optional function to acknowledge an irq from a resource + * + * This function is implemented by the common layer to allow probing of a resource's IRQ. + * @param arg resource-specific data + * @return _MALI_OSK_ERR_OK if the IRQ was successful, or a suitable _mali_osk_errcode_t on failure. */ +typedef _mali_osk_errcode_t (*_mali_osk_irq_ack_t)( void * arg ); + +/** @brief IRQ 'upper-half' handler callback. + * + * This function is implemented by the common layer to do the initial handling of a + * resource's IRQ. This maps on to the concept of an ISR that does the minimum + * work necessary before handing off to an IST. + * + * The communication of the resource-specific data from the ISR to the IST is + * handled by the OSK implementation. + * + * On most systems, the IRQ upper-half handler executes in IRQ context. + * Therefore, the system may have restrictions about what can be done in this + * context + * + * If an IRQ upper-half handler requires more work to be done than can be + * acheived in an IRQ context, then it may defer the work with + * _mali_osk_wq_schedule_work(). Refer to \ref _mali_osk_wq_create_work() for + * more information. + * + * @param arg resource-specific data + * @return _MALI_OSK_ERR_OK if the IRQ was correctly handled, or a suitable + * _mali_osk_errcode_t otherwise. + */ +typedef _mali_osk_errcode_t (*_mali_osk_irq_uhandler_t)( void * arg ); + + +/** @} */ /* end group _mali_osk_irq */ + + +/** @defgroup _mali_osk_atomic OSK Atomic counters + * @{ */ + +/** @brief Public type of atomic counters + * + * This is public for allocation on stack. On systems that support it, this is just a single 32-bit value. + * On others, it could be encapsulating an object stored elsewhere. + * + * Regardless of implementation, the \ref _mali_osk_atomic functions \b must be used + * for all accesses to the variable's value, even if atomicity is not required. + * Do not access u.val or u.obj directly. + */ +typedef struct { + union { + u32 val; + void *obj; + } u; +} _mali_osk_atomic_t; +/** @} */ /* end group _mali_osk_atomic */ + + +/** @defgroup _mali_osk_lock OSK Mutual Exclusion Locks + * @{ */ + + +/** @brief OSK Mutual Exclusion Lock ordered list + * + * This lists the various types of locks in the system and is used to check + * that locks are taken in the correct order. + * + * - Holding more than one lock of the same order at the same time is not + * allowed. + * - Taking a lock of a lower order than the highest-order lock currently held + * is not allowed. + * + */ +typedef enum { + /* || Locks || */ + /* || must be || */ + /* _||_ taken in _||_ */ + /* \ / this \ / */ + /* \/ order! \/ */ + + _MALI_OSK_LOCK_ORDER_FIRST = 0, + + _MALI_OSK_LOCK_ORDER_SESSIONS, + _MALI_OSK_LOCK_ORDER_MEM_SESSION, + _MALI_OSK_LOCK_ORDER_MEM_INFO, + _MALI_OSK_LOCK_ORDER_MEM_PT_CACHE, + _MALI_OSK_LOCK_ORDER_DESCRIPTOR_MAP, + _MALI_OSK_LOCK_ORDER_GROUP_VIRTUAL, + _MALI_OSK_LOCK_ORDER_GROUP, + _MALI_OSK_LOCK_ORDER_TIMELINE_SYSTEM, + _MALI_OSK_LOCK_ORDER_SCHEDULER, + _MALI_OSK_LOCK_ORDER_SCHEDULER_DEFERRED, + _MALI_OSK_LOCK_ORDER_PM_CORE_STATE, + _MALI_OSK_LOCK_ORDER_L2_COMMAND, + _MALI_OSK_LOCK_ORDER_DMA_COMMAND, + _MALI_OSK_LOCK_ORDER_PROFILING, + _MALI_OSK_LOCK_ORDER_L2_COUNTER, + _MALI_OSK_LOCK_ORDER_UTILIZATION, + _MALI_OSK_LOCK_ORDER_PM_EXECUTE, + _MALI_OSK_LOCK_ORDER_SESSION_PENDING_JOBS, + _MALI_OSK_LOCK_ORDER_PM_DOMAIN, + _MALI_OSK_LOCK_ORDER_PMU, + + _MALI_OSK_LOCK_ORDER_LAST, +} _mali_osk_lock_order_t; + + +/** @brief OSK Mutual Exclusion Lock flags type + * + * - Any lock can use the order parameter. + */ +typedef enum { + _MALI_OSK_LOCKFLAG_UNORDERED = 0x1, /**< Indicate that the order of this lock should not be checked */ + _MALI_OSK_LOCKFLAG_ORDERED = 0x2, + /** @enum _mali_osk_lock_flags_t + * + * Flags from 0x10000--0x80000000 are RESERVED for User-mode */ + +} _mali_osk_lock_flags_t; + +/** @brief Mutual Exclusion Lock Mode Optimization hint + * + * The lock mode is used to implement the read/write locking of locks when we call + * functions _mali_osk_mutex_rw_init/wait/signal/term/. In this case, the RO mode can + * be used to allow multiple concurrent readers, but no writers. The RW mode is used for + * writers, and so will wait for all readers to release the lock (if any present). + * Further readers and writers will wait until the writer releases the lock. + * + * The mode is purely an optimization hint: for example, it is permissible for + * all locks to behave in RW mode, regardless of that supplied. + * + * It is an error to attempt to use locks in anything other that RW mode when + * call functions _mali_osk_mutex_rw_wait/signal(). + * + */ +typedef enum { + _MALI_OSK_LOCKMODE_UNDEF = -1, /**< Undefined lock mode. For internal use only */ + _MALI_OSK_LOCKMODE_RW = 0x0, /**< Read-write mode, default. All readers and writers are mutually-exclusive */ + _MALI_OSK_LOCKMODE_RO, /**< Read-only mode, to support multiple concurrent readers, but mutual exclusion in the presence of writers. */ + /** @enum _mali_osk_lock_mode_t + * + * Lock modes 0x40--0x7F are RESERVED for User-mode */ +} _mali_osk_lock_mode_t; + +/** @brief Private types for Mutual Exclusion lock objects */ +typedef struct _mali_osk_lock_debug_s _mali_osk_lock_debug_t; +typedef struct _mali_osk_spinlock_s _mali_osk_spinlock_t; +typedef struct _mali_osk_spinlock_irq_s _mali_osk_spinlock_irq_t; +typedef struct _mali_osk_mutex_s _mali_osk_mutex_t; +typedef struct _mali_osk_mutex_rw_s _mali_osk_mutex_rw_t; + +/** @} */ /* end group _mali_osk_lock */ + +/** @defgroup _mali_osk_low_level_memory OSK Low-level Memory Operations + * @{ */ + +/** + * @brief Private data type for use in IO accesses to/from devices. + * + * This represents some range that is accessible from the device. Examples + * include: + * - Device Registers, which could be readable and/or writeable. + * - Memory that the device has access to, for storing configuration structures. + * + * Access to this range must be made through the _mali_osk_mem_ioread32() and + * _mali_osk_mem_iowrite32() functions. + */ +typedef struct _mali_io_address * mali_io_address; + +/** @defgroup _MALI_OSK_CPU_PAGE CPU Physical page size macros. + * + * The order of the page size is supplied for + * ease of use by algorithms that might require it, since it is easier to know + * it ahead of time rather than calculating it. + * + * The Mali Page Mask macro masks off the lower bits of a physical address to + * give the start address of the page for that physical address. + * + * @note The Mali device driver code is designed for systems with 4KB page size. + * Changing these macros will not make the entire Mali device driver work with + * page sizes other than 4KB. + * + * @note The CPU Physical Page Size has been assumed to be the same as the Mali + * Physical Page Size. + * + * @{ + */ + +/** CPU Page Order, as log to base 2 of the Page size. @see _MALI_OSK_CPU_PAGE_SIZE */ +#define _MALI_OSK_CPU_PAGE_ORDER ((u32)12) +/** CPU Page Size, in bytes. */ +#define _MALI_OSK_CPU_PAGE_SIZE (((u32)1) << (_MALI_OSK_CPU_PAGE_ORDER)) +/** CPU Page Mask, which masks off the offset within a page */ +#define _MALI_OSK_CPU_PAGE_MASK (~((((u32)1) << (_MALI_OSK_CPU_PAGE_ORDER)) - ((u32)1))) +/** @} */ /* end of group _MALI_OSK_CPU_PAGE */ + +/** @defgroup _MALI_OSK_MALI_PAGE Mali Physical Page size macros + * + * Mali Physical page size macros. The order of the page size is supplied for + * ease of use by algorithms that might require it, since it is easier to know + * it ahead of time rather than calculating it. + * + * The Mali Page Mask macro masks off the lower bits of a physical address to + * give the start address of the page for that physical address. + * + * @note The Mali device driver code is designed for systems with 4KB page size. + * Changing these macros will not make the entire Mali device driver work with + * page sizes other than 4KB. + * + * @note The Mali Physical Page Size has been assumed to be the same as the CPU + * Physical Page Size. + * + * @{ + */ + +/** Mali Page Order, as log to base 2 of the Page size. @see _MALI_OSK_MALI_PAGE_SIZE */ +#define _MALI_OSK_MALI_PAGE_ORDER ((u32)12) +/** Mali Page Size, in bytes. */ +#define _MALI_OSK_MALI_PAGE_SIZE (((u32)1) << (_MALI_OSK_MALI_PAGE_ORDER)) +/** Mali Page Mask, which masks off the offset within a page */ +#define _MALI_OSK_MALI_PAGE_MASK (~((((u32)1) << (_MALI_OSK_MALI_PAGE_ORDER)) - ((u32)1))) +/** @} */ /* end of group _MALI_OSK_MALI_PAGE*/ + +/** @brief flags for mapping a user-accessible memory range + * + * Where a function with prefix '_mali_osk_mem_mapregion' accepts flags as one + * of the function parameters, it will use one of these. These allow per-page + * control over mappings. Compare with the mali_memory_allocation_flag type, + * which acts over an entire range + * + * These may be OR'd together with bitwise OR (|), but must be cast back into + * the type after OR'ing. + */ +typedef enum { + _MALI_OSK_MEM_MAPREGION_FLAG_OS_ALLOCATED_PHYSADDR = 0x1, /**< Physical address is OS Allocated */ +} _mali_osk_mem_mapregion_flags_t; +/** @} */ /* end group _mali_osk_low_level_memory */ + +/** @defgroup _mali_osk_notification OSK Notification Queues + * @{ */ + +/** @brief Private type for notification queue objects */ +typedef struct _mali_osk_notification_queue_t_struct _mali_osk_notification_queue_t; + +/** @brief Public notification data object type */ +typedef struct _mali_osk_notification_t_struct { + u32 notification_type; /**< The notification type */ + u32 result_buffer_size; /**< Size of the result buffer to copy to user space */ + void * result_buffer; /**< Buffer containing any type specific data */ +} _mali_osk_notification_t; + +/** @} */ /* end group _mali_osk_notification */ + + +/** @defgroup _mali_osk_timer OSK Timer Callbacks + * @{ */ + +/** @brief Function to call when a timer expires + * + * When a timer expires, this function is called. Note that on many systems, + * a timer callback will be executed in IRQ context. Therefore, restrictions + * may apply on what can be done inside the timer callback. + * + * If a timer requires more work to be done than can be acheived in an IRQ + * context, then it may defer the work with a work-queue. For example, it may + * use \ref _mali_osk_wq_schedule_work() to make use of a bottom-half handler + * to carry out the remaining work. + * + * Stopping the timer with \ref _mali_osk_timer_del() blocks on compeletion of + * the callback. Therefore, the callback may not obtain any mutexes also held + * by any callers of _mali_osk_timer_del(). Otherwise, a deadlock may occur. + * + * @param arg Function-specific data */ +typedef void (*_mali_osk_timer_callback_t)(void * arg); + +/** @brief Private type for Timer Callback Objects */ +typedef struct _mali_osk_timer_t_struct _mali_osk_timer_t; +/** @} */ /* end group _mali_osk_timer */ + + +/** @addtogroup _mali_osk_list OSK Doubly-Linked Circular Lists + * @{ */ + +/** @brief Public List objects. + * + * To use, add a _mali_osk_list_t member to the structure that may become part + * of a list. When traversing the _mali_osk_list_t objects, use the + * _MALI_OSK_CONTAINER_OF() macro to recover the structure from its + *_mali_osk_list_t member + * + * Each structure may have multiple _mali_osk_list_t members, so that the + * structure is part of multiple lists. When traversing lists, ensure that the + * correct _mali_osk_list_t member is used, because type-checking will be + * lost by the compiler. + */ +typedef struct _mali_osk_list_s { + struct _mali_osk_list_s *next; + struct _mali_osk_list_s *prev; +} _mali_osk_list_t; +/** @} */ /* end group _mali_osk_list */ + +/** @addtogroup _mali_osk_miscellaneous + * @{ */ + +/** @brief resource description struct + * + * Platform independent representation of a Mali HW resource + */ +typedef struct _mali_osk_resource { + const char * description; /**< short description of the resource */ + u32 base; /**< Physical base address of the resource, as seen by Mali resources. */ + u32 irq; /**< IRQ number delivered to the CPU, or -1 to tell the driver to probe for it (if possible) */ +} _mali_osk_resource_t; +/** @} */ /* end group _mali_osk_miscellaneous */ + +/** @defgroup _mali_osk_wait_queue OSK Wait Queue functionality + * @{ */ +/** @brief Private type for wait queue objects */ +typedef struct _mali_osk_wait_queue_t_struct _mali_osk_wait_queue_t; +/** @} */ /* end group _mali_osk_wait_queue */ + +/** @} */ /* end group osuapi */ + +/** @} */ /* end group uddapi */ + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_OSK_TYPES_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_pm.c b/drivers/gpu/arm/mali/common/mali_pm.c new file mode 100644 index 00000000000000..7ec2b092c84160 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_pm.c @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2011-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_pm.h" +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_gp_scheduler.h" +#include "mali_pp_scheduler.h" +#include "mali_scheduler.h" +#include "mali_kernel_utilization.h" +#include "mali_group.h" +#include "mali_pm_domain.h" +#include "mali_pmu.h" + +static mali_bool mali_power_on = MALI_FALSE; + +_mali_osk_errcode_t mali_pm_initialize(void) +{ + _mali_osk_pm_dev_enable(); + return _MALI_OSK_ERR_OK; +} + +void mali_pm_terminate(void) +{ + mali_pm_domain_terminate(); + _mali_osk_pm_dev_disable(); +} + +/* Reset GPU after power up */ +static void mali_pm_reset_gpu(void) +{ + /* Reset all L2 caches */ + mali_l2_cache_reset_all(); + + /* Reset all groups */ + mali_scheduler_reset_all_groups(); +} + +void mali_pm_os_suspend(void) +{ + MALI_DEBUG_PRINT(3, ("Mali PM: OS suspend\n")); + mali_gp_scheduler_suspend(); + mali_pp_scheduler_suspend(); + mali_utilization_suspend(); + mali_group_power_off(MALI_TRUE); + mali_power_on = MALI_FALSE; +} + +void mali_pm_os_resume(void) +{ + struct mali_pmu_core *pmu = mali_pmu_get_global_pmu_core(); + mali_bool do_reset = MALI_FALSE; + + MALI_DEBUG_PRINT(3, ("Mali PM: OS resume\n")); + + if (MALI_TRUE != mali_power_on) { + do_reset = MALI_TRUE; + } + + if (NULL != pmu) { + mali_pmu_reset(pmu); + } + + mali_power_on = MALI_TRUE; + _mali_osk_write_mem_barrier(); + + if (do_reset) { + mali_pm_reset_gpu(); + mali_group_power_on(); + } + + mali_gp_scheduler_resume(); + mali_pp_scheduler_resume(); +} + +void mali_pm_runtime_suspend(void) +{ + MALI_DEBUG_PRINT(3, ("Mali PM: Runtime suspend\n")); + mali_group_power_off(MALI_TRUE); + mali_power_on = MALI_FALSE; +} + +void mali_pm_runtime_resume(void) +{ + struct mali_pmu_core *pmu = mali_pmu_get_global_pmu_core(); + mali_bool do_reset = MALI_FALSE; + + MALI_DEBUG_PRINT(3, ("Mali PM: Runtime resume\n")); + + if (MALI_TRUE != mali_power_on) { + do_reset = MALI_TRUE; + } + + if (NULL != pmu) { + mali_pmu_reset(pmu); + } + + mali_power_on = MALI_TRUE; + _mali_osk_write_mem_barrier(); + + if (do_reset) { + mali_pm_reset_gpu(); + mali_group_power_on(); + } +} + +void mali_pm_set_power_is_on(void) +{ + mali_power_on = MALI_TRUE; +} + +mali_bool mali_pm_is_power_on(void) +{ + return mali_power_on; +} diff --git a/drivers/gpu/arm/mali/common/mali_pm.h b/drivers/gpu/arm/mali/common/mali_pm.h new file mode 100644 index 00000000000000..1c72aaf8e63344 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_pm.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2011-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_PM_H__ +#define __MALI_PM_H__ + +#include "mali_osk.h" + +_mali_osk_errcode_t mali_pm_initialize(void); +void mali_pm_terminate(void); + +/* Callback functions registered for the runtime PMM system */ +void mali_pm_os_suspend(void); +void mali_pm_os_resume(void); +void mali_pm_runtime_suspend(void); +void mali_pm_runtime_resume(void); + +void mali_pm_set_power_is_on(void); +mali_bool mali_pm_is_power_on(void); + +#endif /* __MALI_PM_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_pm_domain.c b/drivers/gpu/arm/mali/common/mali_pm_domain.c new file mode 100644 index 00000000000000..0e3f198b71748f --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_pm_domain.c @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_pm_domain.h" +#include "mali_pmu.h" +#include "mali_group.h" + +static struct mali_pm_domain *mali_pm_domains[MALI_MAX_NUMBER_OF_DOMAINS] = { NULL, }; + +static void mali_pm_domain_lock(struct mali_pm_domain *domain) +{ + _mali_osk_spinlock_irq_lock(domain->lock); +} + +static void mali_pm_domain_unlock(struct mali_pm_domain *domain) +{ + _mali_osk_spinlock_irq_unlock(domain->lock); +} + +MALI_STATIC_INLINE void mali_pm_domain_state_set(struct mali_pm_domain *domain, mali_pm_domain_state state) +{ + domain->state = state; +} + +struct mali_pm_domain *mali_pm_domain_create(u32 pmu_mask) +{ + struct mali_pm_domain* domain = NULL; + u32 domain_id = 0; + + domain = mali_pm_domain_get_from_mask(pmu_mask); + if (NULL != domain) return domain; + + MALI_DEBUG_PRINT(2, ("Mali PM domain: Creating Mali PM domain (mask=0x%08X)\n", pmu_mask)); + + domain = (struct mali_pm_domain *)_mali_osk_malloc(sizeof(struct mali_pm_domain)); + if (NULL != domain) { + domain->lock = _mali_osk_spinlock_irq_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_PM_DOMAIN); + if (NULL == domain->lock) { + _mali_osk_free(domain); + return NULL; + } + + domain->state = MALI_PM_DOMAIN_ON; + domain->pmu_mask = pmu_mask; + domain->use_count = 0; + domain->group_list = NULL; + domain->group_count = 0; + domain->l2 = NULL; + + domain_id = _mali_osk_fls(pmu_mask) - 1; + /* Verify the domain_id */ + MALI_DEBUG_ASSERT(MALI_MAX_NUMBER_OF_DOMAINS > domain_id); + /* Verify that pmu_mask only one bit is set */ + MALI_DEBUG_ASSERT((1 << domain_id) == pmu_mask); + mali_pm_domains[domain_id] = domain; + + return domain; + } else { + MALI_DEBUG_PRINT_ERROR(("Unable to create PM domain\n")); + } + + return NULL; +} + +void mali_pm_domain_delete(struct mali_pm_domain *domain) +{ + if (NULL == domain) { + return; + } + _mali_osk_spinlock_irq_term(domain->lock); + + _mali_osk_free(domain); +} + +void mali_pm_domain_terminate(void) +{ + int i; + + /* Delete all domains */ + for (i = 0; i < MALI_MAX_NUMBER_OF_DOMAINS; i++) { + mali_pm_domain_delete(mali_pm_domains[i]); + } +} + +void mali_pm_domain_add_group(u32 mask, struct mali_group *group) +{ + struct mali_pm_domain *domain = mali_pm_domain_get_from_mask(mask); + struct mali_group *next; + + if (NULL == domain) return; + + MALI_DEBUG_ASSERT_POINTER(group); + + ++domain->group_count; + next = domain->group_list; + + domain->group_list = group; + + group->pm_domain_list = next; + + mali_group_set_pm_domain(group, domain); + + /* Get pm domain ref after mali_group_set_pm_domain */ + mali_group_get_pm_domain_ref(group); +} + +void mali_pm_domain_add_l2(u32 mask, struct mali_l2_cache_core *l2) +{ + struct mali_pm_domain *domain = mali_pm_domain_get_from_mask(mask); + + if (NULL == domain) return; + + MALI_DEBUG_ASSERT(NULL == domain->l2); + MALI_DEBUG_ASSERT(NULL != l2); + + domain->l2 = l2; + + mali_l2_cache_set_pm_domain(l2, domain); +} + +struct mali_pm_domain *mali_pm_domain_get_from_mask(u32 mask) +{ + u32 id = 0; + + if (0 == mask) return NULL; + + id = _mali_osk_fls(mask)-1; + + MALI_DEBUG_ASSERT(MALI_MAX_NUMBER_OF_DOMAINS > id); + /* Verify that pmu_mask only one bit is set */ + MALI_DEBUG_ASSERT((1 << id) == mask); + + return mali_pm_domains[id]; +} + +struct mali_pm_domain *mali_pm_domain_get_from_index(u32 id) +{ + MALI_DEBUG_ASSERT(MALI_MAX_NUMBER_OF_DOMAINS > id); + + return mali_pm_domains[id]; +} + +void mali_pm_domain_ref_get(struct mali_pm_domain *domain) +{ + if (NULL == domain) return; + + mali_pm_domain_lock(domain); + ++domain->use_count; + + if (MALI_PM_DOMAIN_ON != domain->state) { + /* Power on */ + struct mali_pmu_core *pmu = mali_pmu_get_global_pmu_core(); + + MALI_DEBUG_PRINT(3, ("PM Domain: Powering on 0x%08x\n", domain->pmu_mask)); + + if (NULL != pmu) { + _mali_osk_errcode_t err; + + err = mali_pmu_power_up(pmu, domain->pmu_mask); + + if (_MALI_OSK_ERR_OK != err && _MALI_OSK_ERR_BUSY != err) { + MALI_PRINT_ERROR(("PM Domain: Failed to power up PM domain 0x%08x\n", + domain->pmu_mask)); + } + } + mali_pm_domain_state_set(domain, MALI_PM_DOMAIN_ON); + } else { + MALI_DEBUG_ASSERT(MALI_PM_DOMAIN_ON == mali_pm_domain_state_get(domain)); + } + + mali_pm_domain_unlock(domain); +} + +void mali_pm_domain_ref_put(struct mali_pm_domain *domain) +{ + if (NULL == domain) return; + + mali_pm_domain_lock(domain); + --domain->use_count; + + if (0 == domain->use_count && MALI_PM_DOMAIN_OFF != domain->state) { + /* Power off */ + struct mali_pmu_core *pmu = mali_pmu_get_global_pmu_core(); + + MALI_DEBUG_PRINT(3, ("PM Domain: Powering off 0x%08x\n", domain->pmu_mask)); + + mali_pm_domain_state_set(domain, MALI_PM_DOMAIN_OFF); + + if (NULL != pmu) { + _mali_osk_errcode_t err; + + err = mali_pmu_power_down(pmu, domain->pmu_mask); + + if (_MALI_OSK_ERR_OK != err && _MALI_OSK_ERR_BUSY != err) { + MALI_PRINT_ERROR(("PM Domain: Failed to power down PM domain 0x%08x\n", + domain->pmu_mask)); + } + } + } + mali_pm_domain_unlock(domain); +} + +mali_bool mali_pm_domain_lock_state(struct mali_pm_domain *domain) +{ + mali_bool is_powered = MALI_TRUE; + + /* Take a reference without powering on */ + if (NULL != domain) { + mali_pm_domain_lock(domain); + ++domain->use_count; + + if (MALI_PM_DOMAIN_ON != domain->state) { + is_powered = MALI_FALSE; + } + mali_pm_domain_unlock(domain); + } + + if(!_mali_osk_pm_dev_ref_add_no_power_on()) { + is_powered = MALI_FALSE; + } + + return is_powered; +} + +void mali_pm_domain_unlock_state(struct mali_pm_domain *domain) +{ + _mali_osk_pm_dev_ref_dec_no_power_on(); + + if (NULL != domain) { + mali_pm_domain_ref_put(domain); + } +} diff --git a/drivers/gpu/arm/mali/common/mali_pm_domain.h b/drivers/gpu/arm/mali/common/mali_pm_domain.h new file mode 100644 index 00000000000000..742f96ec80d4b2 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_pm_domain.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_PM_DOMAIN_H__ +#define __MALI_PM_DOMAIN_H__ + +#include "mali_kernel_common.h" +#include "mali_osk.h" + +#include "mali_l2_cache.h" +#include "mali_group.h" +#include "mali_pmu.h" + +typedef enum { + MALI_PM_DOMAIN_ON, + MALI_PM_DOMAIN_OFF, +} mali_pm_domain_state; + +struct mali_pm_domain { + mali_pm_domain_state state; + _mali_osk_spinlock_irq_t *lock; + + s32 use_count; + + u32 pmu_mask; + + int group_count; + struct mali_group *group_list; + + struct mali_l2_cache_core *l2; +}; + +struct mali_pm_domain *mali_pm_domain_create(u32 pmu_mask); + +void mali_pm_domain_add_group(u32 mask, struct mali_group *group); + +void mali_pm_domain_add_l2(u32 mask, struct mali_l2_cache_core *l2); +void mali_pm_domain_delete(struct mali_pm_domain *domain); + +void mali_pm_domain_terminate(void); + +/** Get PM domain from domain ID + */ +struct mali_pm_domain *mali_pm_domain_get_from_mask(u32 mask); +struct mali_pm_domain *mali_pm_domain_get_from_index(u32 id); + +/* Ref counting */ +void mali_pm_domain_ref_get(struct mali_pm_domain *domain); +void mali_pm_domain_ref_put(struct mali_pm_domain *domain); + +MALI_STATIC_INLINE struct mali_l2_cache_core *mali_pm_domain_l2_get(struct mali_pm_domain *domain) +{ + return domain->l2; +} + +MALI_STATIC_INLINE mali_pm_domain_state mali_pm_domain_state_get(struct mali_pm_domain *domain) +{ + return domain->state; +} + +mali_bool mali_pm_domain_lock_state(struct mali_pm_domain *domain); +void mali_pm_domain_unlock_state(struct mali_pm_domain *domain); + +#define MALI_PM_DOMAIN_FOR_EACH_GROUP(group, domain) for ((group) = (domain)->group_list;\ + NULL != (group); (group) = (group)->pm_domain_list) + +#endif /* __MALI_PM_DOMAIN_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_pmu.c b/drivers/gpu/arm/mali/common/mali_pmu.c new file mode 100644 index 00000000000000..3c27fc7d9e1f0b --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_pmu.c @@ -0,0 +1,406 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_pmu.c + * Mali driver functions for Mali 400 PMU hardware + */ +#include "mali_hw_core.h" +#include "mali_pmu.h" +#include "mali_pp.h" +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_pm.h" +#include "mali_osk_mali.h" + +u16 mali_pmu_global_domain_config[MALI_MAX_NUMBER_OF_DOMAINS]= {0}; + +static u32 mali_pmu_detect_mask(void); + +/** @brief MALI inbuilt PMU hardware info and PMU hardware has knowledge of cores power mask + */ +struct mali_pmu_core { + struct mali_hw_core hw_core; + _mali_osk_spinlock_t *lock; + u32 registered_cores_mask; + u32 active_cores_mask; + u32 switch_delay; +}; + +static struct mali_pmu_core *mali_global_pmu_core = NULL; + +/** @brief Register layout for hardware PMU + */ +typedef enum { + PMU_REG_ADDR_MGMT_POWER_UP = 0x00, /*< Power up register */ + PMU_REG_ADDR_MGMT_POWER_DOWN = 0x04, /*< Power down register */ + PMU_REG_ADDR_MGMT_STATUS = 0x08, /*< Core sleep status register */ + PMU_REG_ADDR_MGMT_INT_MASK = 0x0C, /*< Interrupt mask register */ + PMU_REG_ADDR_MGMT_INT_RAWSTAT = 0x10, /*< Interrupt raw status register */ + PMU_REG_ADDR_MGMT_INT_CLEAR = 0x18, /*< Interrupt clear register */ + PMU_REG_ADDR_MGMT_SW_DELAY = 0x1C, /*< Switch delay register */ + PMU_REGISTER_ADDRESS_SPACE_SIZE = 0x28, /*< Size of register space */ +} pmu_reg_addr_mgmt_addr; + +#define PMU_REG_VAL_IRQ 1 + +struct mali_pmu_core *mali_pmu_create(_mali_osk_resource_t *resource) +{ + struct mali_pmu_core* pmu; + + MALI_DEBUG_ASSERT(NULL == mali_global_pmu_core); + MALI_DEBUG_PRINT(2, ("Mali PMU: Creating Mali PMU core\n")); + + pmu = (struct mali_pmu_core *)_mali_osk_malloc(sizeof(struct mali_pmu_core)); + if (NULL != pmu) { + pmu->lock = _mali_osk_spinlock_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_PMU); + if (NULL != pmu->lock) { + pmu->registered_cores_mask = mali_pmu_detect_mask(); + pmu->active_cores_mask = pmu->registered_cores_mask; + + if (_MALI_OSK_ERR_OK == mali_hw_core_create(&pmu->hw_core, resource, PMU_REGISTER_ADDRESS_SPACE_SIZE)) { + _mali_osk_errcode_t err; + struct _mali_osk_device_data data = { 0, }; + + err = _mali_osk_device_data_get(&data); + if (_MALI_OSK_ERR_OK == err) { + pmu->switch_delay = data.pmu_switch_delay; + mali_global_pmu_core = pmu; + return pmu; + } + mali_hw_core_delete(&pmu->hw_core); + } + _mali_osk_spinlock_term(pmu->lock); + } + _mali_osk_free(pmu); + } + + return NULL; +} + +void mali_pmu_delete(struct mali_pmu_core *pmu) +{ + MALI_DEBUG_ASSERT_POINTER(pmu); + MALI_DEBUG_ASSERT(pmu == mali_global_pmu_core); + MALI_DEBUG_PRINT(2, ("Mali PMU: Deleting Mali PMU core\n")); + + _mali_osk_spinlock_term(pmu->lock); + mali_hw_core_delete(&pmu->hw_core); + _mali_osk_free(pmu); + mali_global_pmu_core = NULL; +} + +static void mali_pmu_lock(struct mali_pmu_core *pmu) +{ + _mali_osk_spinlock_lock(pmu->lock); +} +static void mali_pmu_unlock(struct mali_pmu_core *pmu) +{ + _mali_osk_spinlock_unlock(pmu->lock); +} + +static _mali_osk_errcode_t mali_pmu_wait_for_command_finish(struct mali_pmu_core *pmu) +{ + u32 rawstat; + u32 timeout = MALI_REG_POLL_COUNT_SLOW; + + MALI_DEBUG_ASSERT(pmu); + + /* Wait for the command to complete */ + do { + rawstat = mali_hw_core_register_read(&pmu->hw_core, PMU_REG_ADDR_MGMT_INT_RAWSTAT); + --timeout; + } while (0 == (rawstat & PMU_REG_VAL_IRQ) && 0 < timeout); + + MALI_DEBUG_ASSERT(0 < timeout); + if (0 == timeout) { + return _MALI_OSK_ERR_TIMEOUT; + } + + mali_hw_core_register_write(&pmu->hw_core, PMU_REG_ADDR_MGMT_INT_CLEAR, PMU_REG_VAL_IRQ); + + return _MALI_OSK_ERR_OK; +} + +static _mali_osk_errcode_t mali_pmu_power_up_internal(struct mali_pmu_core *pmu, const u32 mask) +{ + u32 stat; + _mali_osk_errcode_t err; +#if !defined(CONFIG_MALI_PMU_PARALLEL_POWER_UP) + u32 current_domain; +#endif + + MALI_DEBUG_ASSERT_POINTER(pmu); + MALI_DEBUG_ASSERT(0 == (mali_hw_core_register_read(&pmu->hw_core, PMU_REG_ADDR_MGMT_INT_RAWSTAT) + & PMU_REG_VAL_IRQ)); + + stat = mali_hw_core_register_read(&pmu->hw_core, PMU_REG_ADDR_MGMT_STATUS); + stat &= pmu->registered_cores_mask; + if (0 == mask || 0 == (stat & mask)) return _MALI_OSK_ERR_OK; + +#if defined(CONFIG_MALI_PMU_PARALLEL_POWER_UP) + mali_hw_core_register_write(&pmu->hw_core, PMU_REG_ADDR_MGMT_POWER_UP, mask); + + err = mali_pmu_wait_for_command_finish(pmu); + if (_MALI_OSK_ERR_OK != err) { + return err; + } +#else + for (current_domain = 1; current_domain <= pmu->registered_cores_mask; current_domain <<= 1) { + if (current_domain & mask & stat) { + mali_hw_core_register_write(&pmu->hw_core, PMU_REG_ADDR_MGMT_POWER_UP, current_domain); + + err = mali_pmu_wait_for_command_finish(pmu); + if (_MALI_OSK_ERR_OK != err) { + return err; + } + } + } +#endif + +#if defined(DEBUG) + /* Get power status of cores */ + stat = mali_hw_core_register_read(&pmu->hw_core, PMU_REG_ADDR_MGMT_STATUS); + stat &= pmu->registered_cores_mask; + + MALI_DEBUG_ASSERT(0 == (stat & mask)); + MALI_DEBUG_ASSERT(0 == (stat & pmu->active_cores_mask)); +#endif /* defined(DEBUG) */ + + return _MALI_OSK_ERR_OK; +} + +static _mali_osk_errcode_t mali_pmu_power_down_internal(struct mali_pmu_core *pmu, const u32 mask) +{ + u32 stat; + _mali_osk_errcode_t err; + + MALI_DEBUG_ASSERT_POINTER(pmu); + MALI_DEBUG_ASSERT(0 == (mali_hw_core_register_read(&pmu->hw_core, PMU_REG_ADDR_MGMT_INT_RAWSTAT) + & PMU_REG_VAL_IRQ)); + + stat = mali_hw_core_register_read(&pmu->hw_core, PMU_REG_ADDR_MGMT_STATUS); + stat &= pmu->registered_cores_mask; + + if (0 == mask || 0 == ((~stat) & mask)) return _MALI_OSK_ERR_OK; + + mali_hw_core_register_write(&pmu->hw_core, PMU_REG_ADDR_MGMT_POWER_DOWN, mask); + + /* Do not wait for interrupt on Mali-300/400 if all domains are powered off + * by our power down command, because the HW will simply not generate an + * interrupt in this case.*/ + if (mali_is_mali450() || pmu->registered_cores_mask != (mask | stat)) { + err = mali_pmu_wait_for_command_finish(pmu); + if (_MALI_OSK_ERR_OK != err) { + return err; + } + } else { + mali_hw_core_register_write(&pmu->hw_core, PMU_REG_ADDR_MGMT_INT_CLEAR, PMU_REG_VAL_IRQ); + } +#if defined(DEBUG) + /* Get power status of cores */ + stat = mali_hw_core_register_read(&pmu->hw_core, PMU_REG_ADDR_MGMT_STATUS); + stat &= pmu->registered_cores_mask; + + MALI_DEBUG_ASSERT(mask == (stat & mask)); +#endif + + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t mali_pmu_reset(struct mali_pmu_core *pmu) +{ + _mali_osk_errcode_t err; + u32 cores_off_mask, cores_on_mask, stat; + + mali_pmu_lock(pmu); + + /* Setup the desired defaults */ + mali_hw_core_register_write_relaxed(&pmu->hw_core, PMU_REG_ADDR_MGMT_INT_MASK, 0); + mali_hw_core_register_write_relaxed(&pmu->hw_core, PMU_REG_ADDR_MGMT_SW_DELAY, pmu->switch_delay); + + /* Get power status of cores */ + stat = mali_hw_core_register_read(&pmu->hw_core, PMU_REG_ADDR_MGMT_STATUS); + + cores_off_mask = pmu->registered_cores_mask & ~(stat | pmu->active_cores_mask); + cores_on_mask = pmu->registered_cores_mask & (stat & pmu->active_cores_mask); + + if (0 != cores_off_mask) { + err = mali_pmu_power_down_internal(pmu, cores_off_mask); + if (_MALI_OSK_ERR_OK != err) return err; + } + + if (0 != cores_on_mask) { + err = mali_pmu_power_up_internal(pmu, cores_on_mask); + if (_MALI_OSK_ERR_OK != err) return err; + } + +#if defined(DEBUG) + { + stat = mali_hw_core_register_read(&pmu->hw_core, PMU_REG_ADDR_MGMT_STATUS); + stat &= pmu->registered_cores_mask; + + MALI_DEBUG_ASSERT(stat == (pmu->registered_cores_mask & ~pmu->active_cores_mask)); + } +#endif /* defined(DEBUG) */ + + mali_pmu_unlock(pmu); + + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t mali_pmu_power_down(struct mali_pmu_core *pmu, u32 mask) +{ + _mali_osk_errcode_t err; + + MALI_DEBUG_ASSERT_POINTER(pmu); + MALI_DEBUG_ASSERT(pmu->registered_cores_mask != 0 ); + + /* Make sure we have a valid power domain mask */ + if (mask > pmu->registered_cores_mask) { + return _MALI_OSK_ERR_INVALID_ARGS; + } + + mali_pmu_lock(pmu); + + MALI_DEBUG_PRINT(4, ("Mali PMU: Power down (0x%08X)\n", mask)); + + pmu->active_cores_mask &= ~mask; + + _mali_osk_pm_dev_ref_add_no_power_on(); + if (!mali_pm_is_power_on()) { + /* Don't touch hardware if all of Mali is powered off. */ + _mali_osk_pm_dev_ref_dec_no_power_on(); + mali_pmu_unlock(pmu); + + MALI_DEBUG_PRINT(4, ("Mali PMU: Skipping power down (0x%08X) since Mali is off\n", mask)); + + return _MALI_OSK_ERR_BUSY; + } + + err = mali_pmu_power_down_internal(pmu, mask); + + _mali_osk_pm_dev_ref_dec_no_power_on(); + mali_pmu_unlock(pmu); + + return err; +} + +_mali_osk_errcode_t mali_pmu_power_up(struct mali_pmu_core *pmu, u32 mask) +{ + _mali_osk_errcode_t err; + + MALI_DEBUG_ASSERT_POINTER(pmu); + MALI_DEBUG_ASSERT(pmu->registered_cores_mask != 0 ); + + /* Make sure we have a valid power domain mask */ + if (mask & ~pmu->registered_cores_mask) { + return _MALI_OSK_ERR_INVALID_ARGS; + } + + mali_pmu_lock(pmu); + + MALI_DEBUG_PRINT(4, ("Mali PMU: Power up (0x%08X)\n", mask)); + + pmu->active_cores_mask |= mask; + + _mali_osk_pm_dev_ref_add_no_power_on(); + if (!mali_pm_is_power_on()) { + /* Don't touch hardware if all of Mali is powered off. */ + _mali_osk_pm_dev_ref_dec_no_power_on(); + mali_pmu_unlock(pmu); + + MALI_DEBUG_PRINT(4, ("Mali PMU: Skipping power up (0x%08X) since Mali is off\n", mask)); + + return _MALI_OSK_ERR_BUSY; + } + + err = mali_pmu_power_up_internal(pmu, mask); + + _mali_osk_pm_dev_ref_dec_no_power_on(); + mali_pmu_unlock(pmu); + + return err; +} + +_mali_osk_errcode_t mali_pmu_power_down_all(struct mali_pmu_core *pmu) +{ + _mali_osk_errcode_t err; + + MALI_DEBUG_ASSERT_POINTER(pmu); + MALI_DEBUG_ASSERT(pmu->registered_cores_mask != 0); + + mali_pmu_lock(pmu); + + /* Setup the desired defaults in case we were called before mali_pmu_reset() */ + mali_hw_core_register_write_relaxed(&pmu->hw_core, PMU_REG_ADDR_MGMT_INT_MASK, 0); + mali_hw_core_register_write_relaxed(&pmu->hw_core, PMU_REG_ADDR_MGMT_SW_DELAY, pmu->switch_delay); + + err = mali_pmu_power_down_internal(pmu, pmu->registered_cores_mask); + + mali_pmu_unlock(pmu); + + return err; +} + +_mali_osk_errcode_t mali_pmu_power_up_all(struct mali_pmu_core *pmu) +{ + _mali_osk_errcode_t err; + + MALI_DEBUG_ASSERT_POINTER(pmu); + MALI_DEBUG_ASSERT(pmu->registered_cores_mask != 0); + + mali_pmu_lock(pmu); + + /* Setup the desired defaults in case we were called before mali_pmu_reset() */ + mali_hw_core_register_write_relaxed(&pmu->hw_core, PMU_REG_ADDR_MGMT_INT_MASK, 0); + mali_hw_core_register_write_relaxed(&pmu->hw_core, PMU_REG_ADDR_MGMT_SW_DELAY, pmu->switch_delay); + + err = mali_pmu_power_up_internal(pmu, pmu->active_cores_mask); + + mali_pmu_unlock(pmu); + return err; +} + +struct mali_pmu_core *mali_pmu_get_global_pmu_core(void) +{ + return mali_global_pmu_core; +} + +static u32 mali_pmu_detect_mask(void) +{ + int dynamic_config_pp = 0; + int dynamic_config_l2 = 0; + int i = 0; + u32 mask = 0; + + /* Check if PM domain compatible with actually pp core and l2 cache and collection info about domain */ + mask = mali_pmu_get_domain_mask(MALI_GP_DOMAIN_INDEX); + + for (i = MALI_PP0_DOMAIN_INDEX; i <= MALI_PP7_DOMAIN_INDEX; i++) { + mask |= mali_pmu_get_domain_mask(i); + + if (0x0 != mali_pmu_get_domain_mask(i)) { + dynamic_config_pp++; + } + } + + for (i = MALI_L20_DOMAIN_INDEX; i <= MALI_L22_DOMAIN_INDEX; i++) { + mask |= mali_pmu_get_domain_mask(i); + + if (0x0 != mali_pmu_get_domain_mask(i)) { + dynamic_config_l2++; + } + } + + MALI_DEBUG_PRINT(2, ("Mali PMU: mask 0x%x, pp_core %d, l2_core %d \n", mask, dynamic_config_pp, dynamic_config_l2)); + + return mask; +} diff --git a/drivers/gpu/arm/mali/common/mali_pmu.h b/drivers/gpu/arm/mali/common/mali_pmu.h new file mode 100644 index 00000000000000..f16d67a8834445 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_pmu.h @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_platform.h + * Platform specific Mali driver functions + */ + +#ifndef __MALI_PMU_H__ +#define __MALI_PMU_H__ + +#include "mali_osk.h" + +#define MALI_GP_DOMAIN_INDEX 0 +#define MALI_PP0_DOMAIN_INDEX 1 +#define MALI_PP1_DOMAIN_INDEX 2 +#define MALI_PP2_DOMAIN_INDEX 3 +#define MALI_PP3_DOMAIN_INDEX 4 +#define MALI_PP4_DOMAIN_INDEX 5 +#define MALI_PP5_DOMAIN_INDEX 6 +#define MALI_PP6_DOMAIN_INDEX 7 +#define MALI_PP7_DOMAIN_INDEX 8 +#define MALI_L20_DOMAIN_INDEX 9 +#define MALI_L21_DOMAIN_INDEX 10 +#define MALI_L22_DOMAIN_INDEX 11 + +#define MALI_MAX_NUMBER_OF_DOMAINS 12 + +/* Record the domain config from the customer or default config */ +extern u16 mali_pmu_global_domain_config[]; + +static inline u16 mali_pmu_get_domain_mask(u32 index) +{ + MALI_DEBUG_ASSERT(MALI_MAX_NUMBER_OF_DOMAINS > index); + + return mali_pmu_global_domain_config[index]; +} + +static inline void mali_pmu_set_domain_mask(u32 index, u16 value) +{ + MALI_DEBUG_ASSERT(MALI_MAX_NUMBER_OF_DOMAINS > index); + + mali_pmu_global_domain_config[index] = value; +} + +static inline void mali_pmu_copy_domain_mask(void *src, u32 len) +{ + _mali_osk_memcpy(mali_pmu_global_domain_config, src, len); +} + +struct mali_pmu_core; + +/** @brief Initialisation of MALI PMU + * + * This is called from entry point of the driver in order to create and intialize the PMU resource + * + * @param resource it will be a pointer to a PMU resource + * @param number_of_pp_cores Number of found PP resources in configuration + * @param number_of_l2_caches Number of found L2 cache resources in configuration + * @return The created PMU object, or NULL in case of failure. + */ +struct mali_pmu_core *mali_pmu_create(_mali_osk_resource_t *resource); + +/** @brief It deallocates the PMU resource + * + * This is called on the exit of the driver to terminate the PMU resource + * + * @param pmu Pointer to PMU core object to delete + */ +void mali_pmu_delete(struct mali_pmu_core *pmu); + +/** @brief Reset PMU core + * + * @param pmu Pointer to PMU core object to reset + * @return _MALI_OSK_ERR_OK on success, otherwise failure. + */ +_mali_osk_errcode_t mali_pmu_reset(struct mali_pmu_core *pmu); + +/** @brief MALI GPU power down using MALI in-built PMU + * + * Called to power down the specified cores. The mask will be saved so that \a + * mali_pmu_power_up_all will bring the PMU back to the previous state set with + * this function or \a mali_pmu_power_up. + * + * @param pmu Pointer to PMU core object to power down + * @param mask Mask specifying which power domains to power down + * @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error. + */ +_mali_osk_errcode_t mali_pmu_power_down(struct mali_pmu_core *pmu, u32 mask); + +/** @brief MALI GPU power up using MALI in-built PMU + * + * Called to power up the specified cores. The mask will be saved so that \a + * mali_pmu_power_up_all will bring the PMU back to the previous state set with + * this function or \a mali_pmu_power_down. + * + * @param pmu Pointer to PMU core object to power up + * @param mask Mask specifying which power domains to power up + * @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error. + */ +_mali_osk_errcode_t mali_pmu_power_up(struct mali_pmu_core *pmu, u32 mask); + +/** @brief MALI GPU power down using MALI in-built PMU + * + * called to power down all cores + * + * @param pmu Pointer to PMU core object to power down + * @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error. + */ +_mali_osk_errcode_t mali_pmu_power_down_all(struct mali_pmu_core *pmu); + +/** @brief MALI GPU power up using MALI in-built PMU + * + * called to power up all cores + * + * @param pmu Pointer to PMU core object to power up + * @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error. + */ +_mali_osk_errcode_t mali_pmu_power_up_all(struct mali_pmu_core *pmu); + +/** @brief Retrieves the Mali PMU core object (if any) + * + * @return The Mali PMU object, or NULL if no PMU exists. + */ +struct mali_pmu_core *mali_pmu_get_global_pmu_core(void); + +#endif /* __MALI_PMU_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_pp.c b/drivers/gpu/arm/mali/common/mali_pp.c new file mode 100644 index 00000000000000..0f481375e31bbc --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_pp.c @@ -0,0 +1,573 @@ +/* + * Copyright (C) 2011-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_pp_job.h" +#include "mali_pp.h" +#include "mali_hw_core.h" +#include "mali_group.h" +#include "regs/mali_200_regs.h" +#include "mali_kernel_common.h" +#include "mali_kernel_core.h" +#include "mali_dma.h" +#if defined(CONFIG_MALI400_PROFILING) +#include "mali_osk_profiling.h" +#endif + +/* Number of frame registers on Mali-200 */ +#define MALI_PP_MALI200_NUM_FRAME_REGISTERS ((0x04C/4)+1) +/* Number of frame registers on Mali-300 and later */ +#define MALI_PP_MALI400_NUM_FRAME_REGISTERS ((0x058/4)+1) + +static struct mali_pp_core* mali_global_pp_cores[MALI_MAX_NUMBER_OF_PP_CORES] = { NULL }; +static u32 mali_global_num_pp_cores = 0; + +/* Interrupt handlers */ +static void mali_pp_irq_probe_trigger(void *data); +static _mali_osk_errcode_t mali_pp_irq_probe_ack(void *data); + +struct mali_pp_core *mali_pp_create(const _mali_osk_resource_t *resource, struct mali_group *group, mali_bool is_virtual, u32 bcast_id) +{ + struct mali_pp_core* core = NULL; + + MALI_DEBUG_PRINT(2, ("Mali PP: Creating Mali PP core: %s\n", resource->description)); + MALI_DEBUG_PRINT(2, ("Mali PP: Base address of PP core: 0x%x\n", resource->base)); + + if (mali_global_num_pp_cores >= MALI_MAX_NUMBER_OF_PP_CORES) { + MALI_PRINT_ERROR(("Mali PP: Too many PP core objects created\n")); + return NULL; + } + + core = _mali_osk_malloc(sizeof(struct mali_pp_core)); + if (NULL != core) { + core->core_id = mali_global_num_pp_cores; + core->bcast_id = bcast_id; + + if (_MALI_OSK_ERR_OK == mali_hw_core_create(&core->hw_core, resource, MALI200_REG_SIZEOF_REGISTER_BANK)) { + _mali_osk_errcode_t ret; + + if (!is_virtual) { + ret = mali_pp_reset(core); + } else { + ret = _MALI_OSK_ERR_OK; + } + + if (_MALI_OSK_ERR_OK == ret) { + ret = mali_group_add_pp_core(group, core); + if (_MALI_OSK_ERR_OK == ret) { + /* Setup IRQ handlers (which will do IRQ probing if needed) */ + MALI_DEBUG_ASSERT(!is_virtual || -1 != resource->irq); + + core->irq = _mali_osk_irq_init(resource->irq, + mali_group_upper_half_pp, + group, + mali_pp_irq_probe_trigger, + mali_pp_irq_probe_ack, + core, + resource->description); + if (NULL != core->irq) { + mali_global_pp_cores[mali_global_num_pp_cores] = core; + mali_global_num_pp_cores++; + + return core; + } else { + MALI_PRINT_ERROR(("Mali PP: Failed to setup interrupt handlers for PP core %s\n", core->hw_core.description)); + } + mali_group_remove_pp_core(group); + } else { + MALI_PRINT_ERROR(("Mali PP: Failed to add core %s to group\n", core->hw_core.description)); + } + } + mali_hw_core_delete(&core->hw_core); + } + + _mali_osk_free(core); + } else { + MALI_PRINT_ERROR(("Mali PP: Failed to allocate memory for PP core\n")); + } + + return NULL; +} + +void mali_pp_delete(struct mali_pp_core *core) +{ + u32 i; + + MALI_DEBUG_ASSERT_POINTER(core); + + _mali_osk_irq_term(core->irq); + mali_hw_core_delete(&core->hw_core); + + /* Remove core from global list */ + for (i = 0; i < mali_global_num_pp_cores; i++) { + if (mali_global_pp_cores[i] == core) { + mali_global_pp_cores[i] = NULL; + mali_global_num_pp_cores--; + + if (i != mali_global_num_pp_cores) { + /* We removed a PP core from the middle of the array -- move the last + * PP core to the current position to close the gap */ + mali_global_pp_cores[i] = mali_global_pp_cores[mali_global_num_pp_cores]; + mali_global_pp_cores[mali_global_num_pp_cores] = NULL; + } + + break; + } + } + + _mali_osk_free(core); +} + +void mali_pp_stop_bus(struct mali_pp_core *core) +{ + MALI_DEBUG_ASSERT_POINTER(core); + /* Will only send the stop bus command, and not wait for it to complete */ + mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_CTRL_MGMT, MALI200_REG_VAL_CTRL_MGMT_STOP_BUS); +} + +_mali_osk_errcode_t mali_pp_stop_bus_wait(struct mali_pp_core *core) +{ + int i; + + MALI_DEBUG_ASSERT_POINTER(core); + + /* Send the stop bus command. */ + mali_pp_stop_bus(core); + + /* Wait for bus to be stopped */ + for (i = 0; i < MALI_REG_POLL_COUNT_FAST; i++) { + if (mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_STATUS) & MALI200_REG_VAL_STATUS_BUS_STOPPED) + break; + } + + if (MALI_REG_POLL_COUNT_FAST == i) { + MALI_PRINT_ERROR(("Mali PP: Failed to stop bus on %s. Status: 0x%08x\n", core->hw_core.description, mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_STATUS))); + return _MALI_OSK_ERR_FAULT; + } + return _MALI_OSK_ERR_OK; +} + +/* Frame register reset values. + * Taken from the Mali400 TRM, 3.6. Pixel processor control register summary */ +static const u32 mali_frame_registers_reset_values[_MALI_PP_MAX_FRAME_REGISTERS] = { + 0x0, /* Renderer List Address Register */ + 0x0, /* Renderer State Word Base Address Register */ + 0x0, /* Renderer Vertex Base Register */ + 0x2, /* Feature Enable Register */ + 0x0, /* Z Clear Value Register */ + 0x0, /* Stencil Clear Value Register */ + 0x0, /* ABGR Clear Value 0 Register */ + 0x0, /* ABGR Clear Value 1 Register */ + 0x0, /* ABGR Clear Value 2 Register */ + 0x0, /* ABGR Clear Value 3 Register */ + 0x0, /* Bounding Box Left Right Register */ + 0x0, /* Bounding Box Bottom Register */ + 0x0, /* FS Stack Address Register */ + 0x0, /* FS Stack Size and Initial Value Register */ + 0x0, /* Reserved */ + 0x0, /* Reserved */ + 0x0, /* Origin Offset X Register */ + 0x0, /* Origin Offset Y Register */ + 0x75, /* Subpixel Specifier Register */ + 0x0, /* Tiebreak mode Register */ + 0x0, /* Polygon List Format Register */ + 0x0, /* Scaling Register */ + 0x0 /* Tilebuffer configuration Register */ +}; + +/* WBx register reset values */ +static const u32 mali_wb_registers_reset_values[_MALI_PP_MAX_WB_REGISTERS] = { + 0x0, /* WBx Source Select Register */ + 0x0, /* WBx Target Address Register */ + 0x0, /* WBx Target Pixel Format Register */ + 0x0, /* WBx Target AA Format Register */ + 0x0, /* WBx Target Layout */ + 0x0, /* WBx Target Scanline Length */ + 0x0, /* WBx Target Flags Register */ + 0x0, /* WBx MRT Enable Register */ + 0x0, /* WBx MRT Offset Register */ + 0x0, /* WBx Global Test Enable Register */ + 0x0, /* WBx Global Test Reference Value Register */ + 0x0 /* WBx Global Test Compare Function Register */ +}; + +/* Performance Counter 0 Enable Register reset value */ +static const u32 mali_perf_cnt_enable_reset_value = 0; + +_mali_osk_errcode_t mali_pp_hard_reset(struct mali_pp_core *core) +{ + /* Bus must be stopped before calling this function */ + const u32 reset_invalid_value = 0xC0FFE000; + const u32 reset_check_value = 0xC01A0000; + int i; + + MALI_DEBUG_ASSERT_POINTER(core); + MALI_DEBUG_PRINT(2, ("Mali PP: Hard reset of core %s\n", core->hw_core.description)); + + /* Set register to a bogus value. The register will be used to detect when reset is complete */ + mali_hw_core_register_write_relaxed(&core->hw_core, MALI200_REG_ADDR_MGMT_WRITE_BOUNDARY_LOW, reset_invalid_value); + mali_hw_core_register_write_relaxed(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_MASK, MALI200_REG_VAL_IRQ_MASK_NONE); + + /* Force core to reset */ + mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_CTRL_MGMT, MALI200_REG_VAL_CTRL_MGMT_FORCE_RESET); + + /* Wait for reset to be complete */ + for (i = 0; i < MALI_REG_POLL_COUNT_FAST; i++) { + mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_WRITE_BOUNDARY_LOW, reset_check_value); + if (reset_check_value == mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_WRITE_BOUNDARY_LOW)) { + break; + } + } + + if (MALI_REG_POLL_COUNT_FAST == i) { + MALI_PRINT_ERROR(("Mali PP: The hard reset loop didn't work, unable to recover\n")); + } + + mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_WRITE_BOUNDARY_LOW, 0x00000000); /* set it back to the default */ + /* Re-enable interrupts */ + mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_CLEAR, MALI200_REG_VAL_IRQ_MASK_ALL); + mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_MASK, MALI200_REG_VAL_IRQ_MASK_USED); + + return _MALI_OSK_ERR_OK; +} + +void mali_pp_reset_async(struct mali_pp_core *core) +{ + MALI_DEBUG_ASSERT_POINTER(core); + + MALI_DEBUG_PRINT(4, ("Mali PP: Reset of core %s\n", core->hw_core.description)); + + mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_MASK, 0); /* disable the IRQs */ + mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_RAWSTAT, MALI200_REG_VAL_IRQ_MASK_ALL); + mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_CTRL_MGMT, MALI400PP_REG_VAL_CTRL_MGMT_SOFT_RESET); +} + +_mali_osk_errcode_t mali_pp_reset_wait(struct mali_pp_core *core) +{ + int i; + u32 rawstat = 0; + + for (i = 0; i < MALI_REG_POLL_COUNT_FAST; i++) { + if (!(mali_pp_read_status(core) & MALI200_REG_VAL_STATUS_RENDERING_ACTIVE)) { + rawstat = mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_RAWSTAT); + if (rawstat == MALI400PP_REG_VAL_IRQ_RESET_COMPLETED) { + break; + } + } + } + + if (i == MALI_REG_POLL_COUNT_FAST) { + MALI_PRINT_ERROR(("Mali PP: Failed to reset core %s, rawstat: 0x%08x\n", + core->hw_core.description, rawstat)); + return _MALI_OSK_ERR_FAULT; + } + + /* Re-enable interrupts */ + mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_CLEAR, MALI200_REG_VAL_IRQ_MASK_ALL); + mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_MASK, MALI200_REG_VAL_IRQ_MASK_USED); + + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t mali_pp_reset(struct mali_pp_core *core) +{ + mali_pp_reset_async(core); + return mali_pp_reset_wait(core); +} + +void mali_pp_job_dma_cmd_prepare(struct mali_pp_core *core, struct mali_pp_job *job, u32 sub_job, + mali_bool restart_virtual, mali_dma_cmd_buf *buf) +{ + u32 relative_address; + u32 start_index; + u32 nr_of_regs; + u32 *frame_registers = mali_pp_job_get_frame_registers(job); + u32 *wb0_registers = mali_pp_job_get_wb0_registers(job); + u32 *wb1_registers = mali_pp_job_get_wb1_registers(job); + u32 *wb2_registers = mali_pp_job_get_wb2_registers(job); + u32 counter_src0 = mali_pp_job_get_perf_counter_src0(job, sub_job); + u32 counter_src1 = mali_pp_job_get_perf_counter_src1(job, sub_job); + + MALI_DEBUG_ASSERT_POINTER(core); + + /* Write frame registers */ + + /* + * There are two frame registers which are different for each sub job: + * 1. The Renderer List Address Register (MALI200_REG_ADDR_FRAME) + * 2. The FS Stack Address Register (MALI200_REG_ADDR_STACK) + */ + mali_dma_write_conditional(buf, &core->hw_core, MALI200_REG_ADDR_FRAME, mali_pp_job_get_addr_frame(job, sub_job), mali_frame_registers_reset_values[MALI200_REG_ADDR_FRAME / sizeof(u32)]); + + /* For virtual jobs, the stack address shouldn't be broadcast but written individually */ + if (!mali_pp_job_is_virtual(job) || restart_virtual) { + mali_dma_write_conditional(buf, &core->hw_core, MALI200_REG_ADDR_STACK, mali_pp_job_get_addr_stack(job, sub_job), mali_frame_registers_reset_values[MALI200_REG_ADDR_STACK / sizeof(u32)]); + } + + /* Write registers between MALI200_REG_ADDR_FRAME and MALI200_REG_ADDR_STACK */ + relative_address = MALI200_REG_ADDR_RSW; + start_index = MALI200_REG_ADDR_RSW / sizeof(u32); + nr_of_regs = (MALI200_REG_ADDR_STACK - MALI200_REG_ADDR_RSW) / sizeof(u32); + + mali_dma_write_array_conditional(buf, &core->hw_core, + relative_address, &frame_registers[start_index], + nr_of_regs, &mali_frame_registers_reset_values[start_index]); + + /* MALI200_REG_ADDR_STACK_SIZE */ + relative_address = MALI200_REG_ADDR_STACK_SIZE; + start_index = MALI200_REG_ADDR_STACK_SIZE / sizeof(u32); + + mali_dma_write_conditional(buf, &core->hw_core, + relative_address, frame_registers[start_index], + mali_frame_registers_reset_values[start_index]); + + /* Skip 2 reserved registers */ + + /* Write remaining registers */ + relative_address = MALI200_REG_ADDR_ORIGIN_OFFSET_X; + start_index = MALI200_REG_ADDR_ORIGIN_OFFSET_X / sizeof(u32); + nr_of_regs = MALI_PP_MALI400_NUM_FRAME_REGISTERS - MALI200_REG_ADDR_ORIGIN_OFFSET_X / sizeof(u32); + + mali_dma_write_array_conditional(buf, &core->hw_core, + relative_address, &frame_registers[start_index], + nr_of_regs, &mali_frame_registers_reset_values[start_index]); + + /* Write WBx registers */ + if (wb0_registers[0]) { /* M200_WB0_REG_SOURCE_SELECT register */ + mali_dma_write_array_conditional(buf, &core->hw_core, MALI200_REG_ADDR_WB0, wb0_registers, _MALI_PP_MAX_WB_REGISTERS, mali_wb_registers_reset_values); + } + + if (wb1_registers[0]) { /* M200_WB1_REG_SOURCE_SELECT register */ + mali_dma_write_array_conditional(buf, &core->hw_core, MALI200_REG_ADDR_WB1, wb1_registers, _MALI_PP_MAX_WB_REGISTERS, mali_wb_registers_reset_values); + } + + if (wb2_registers[0]) { /* M200_WB2_REG_SOURCE_SELECT register */ + mali_dma_write_array_conditional(buf, &core->hw_core, MALI200_REG_ADDR_WB2, wb2_registers, _MALI_PP_MAX_WB_REGISTERS, mali_wb_registers_reset_values); + } + + if (MALI_HW_CORE_NO_COUNTER != counter_src0) { + mali_dma_write(buf, &core->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_0_SRC, counter_src0); + mali_dma_write_conditional(buf, &core->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_0_ENABLE, MALI200_REG_VAL_PERF_CNT_ENABLE, mali_perf_cnt_enable_reset_value); + } + if (MALI_HW_CORE_NO_COUNTER != counter_src1) { + mali_dma_write(buf, &core->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_1_SRC, counter_src1); + mali_dma_write_conditional(buf, &core->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_1_ENABLE, MALI200_REG_VAL_PERF_CNT_ENABLE, mali_perf_cnt_enable_reset_value); + } + + /* This is the command that starts the core. */ + mali_dma_write(buf, &core->hw_core, MALI200_REG_ADDR_MGMT_CTRL_MGMT, MALI200_REG_VAL_CTRL_MGMT_START_RENDERING); +} + +void mali_pp_job_start(struct mali_pp_core *core, struct mali_pp_job *job, u32 sub_job, mali_bool restart_virtual) +{ + u32 relative_address; + u32 start_index; + u32 nr_of_regs; + u32 *frame_registers = mali_pp_job_get_frame_registers(job); + u32 *wb0_registers = mali_pp_job_get_wb0_registers(job); + u32 *wb1_registers = mali_pp_job_get_wb1_registers(job); + u32 *wb2_registers = mali_pp_job_get_wb2_registers(job); + u32 counter_src0 = mali_pp_job_get_perf_counter_src0(job, sub_job); + u32 counter_src1 = mali_pp_job_get_perf_counter_src1(job, sub_job); + + MALI_DEBUG_ASSERT_POINTER(core); + + /* Write frame registers */ + + /* + * There are two frame registers which are different for each sub job: + * 1. The Renderer List Address Register (MALI200_REG_ADDR_FRAME) + * 2. The FS Stack Address Register (MALI200_REG_ADDR_STACK) + */ + mali_hw_core_register_write_relaxed_conditional(&core->hw_core, MALI200_REG_ADDR_FRAME, mali_pp_job_get_addr_frame(job, sub_job), mali_frame_registers_reset_values[MALI200_REG_ADDR_FRAME / sizeof(u32)]); + + /* For virtual jobs, the stack address shouldn't be broadcast but written individually */ + if (!mali_pp_job_is_virtual(job) || restart_virtual) { + mali_hw_core_register_write_relaxed_conditional(&core->hw_core, MALI200_REG_ADDR_STACK, mali_pp_job_get_addr_stack(job, sub_job), mali_frame_registers_reset_values[MALI200_REG_ADDR_STACK / sizeof(u32)]); + } + + /* Write registers between MALI200_REG_ADDR_FRAME and MALI200_REG_ADDR_STACK */ + relative_address = MALI200_REG_ADDR_RSW; + start_index = MALI200_REG_ADDR_RSW / sizeof(u32); + nr_of_regs = (MALI200_REG_ADDR_STACK - MALI200_REG_ADDR_RSW) / sizeof(u32); + + mali_hw_core_register_write_array_relaxed_conditional(&core->hw_core, + relative_address, &frame_registers[start_index], + nr_of_regs, &mali_frame_registers_reset_values[start_index]); + + /* MALI200_REG_ADDR_STACK_SIZE */ + relative_address = MALI200_REG_ADDR_STACK_SIZE; + start_index = MALI200_REG_ADDR_STACK_SIZE / sizeof(u32); + + mali_hw_core_register_write_relaxed_conditional(&core->hw_core, + relative_address, frame_registers[start_index], + mali_frame_registers_reset_values[start_index]); + + /* Skip 2 reserved registers */ + + /* Write remaining registers */ + relative_address = MALI200_REG_ADDR_ORIGIN_OFFSET_X; + start_index = MALI200_REG_ADDR_ORIGIN_OFFSET_X / sizeof(u32); + nr_of_regs = MALI_PP_MALI400_NUM_FRAME_REGISTERS - MALI200_REG_ADDR_ORIGIN_OFFSET_X / sizeof(u32); + + mali_hw_core_register_write_array_relaxed_conditional(&core->hw_core, + relative_address, &frame_registers[start_index], + nr_of_regs, &mali_frame_registers_reset_values[start_index]); + + /* Write WBx registers */ + if (wb0_registers[0]) { /* M200_WB0_REG_SOURCE_SELECT register */ + mali_hw_core_register_write_array_relaxed_conditional(&core->hw_core, MALI200_REG_ADDR_WB0, wb0_registers, _MALI_PP_MAX_WB_REGISTERS, mali_wb_registers_reset_values); + } + + if (wb1_registers[0]) { /* M200_WB1_REG_SOURCE_SELECT register */ + mali_hw_core_register_write_array_relaxed_conditional(&core->hw_core, MALI200_REG_ADDR_WB1, wb1_registers, _MALI_PP_MAX_WB_REGISTERS, mali_wb_registers_reset_values); + } + + if (wb2_registers[0]) { /* M200_WB2_REG_SOURCE_SELECT register */ + mali_hw_core_register_write_array_relaxed_conditional(&core->hw_core, MALI200_REG_ADDR_WB2, wb2_registers, _MALI_PP_MAX_WB_REGISTERS, mali_wb_registers_reset_values); + } + + if (MALI_HW_CORE_NO_COUNTER != counter_src0) { + mali_hw_core_register_write_relaxed(&core->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_0_SRC, counter_src0); + mali_hw_core_register_write_relaxed_conditional(&core->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_0_ENABLE, MALI200_REG_VAL_PERF_CNT_ENABLE, mali_perf_cnt_enable_reset_value); + } + if (MALI_HW_CORE_NO_COUNTER != counter_src1) { + mali_hw_core_register_write_relaxed(&core->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_1_SRC, counter_src1); + mali_hw_core_register_write_relaxed_conditional(&core->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_1_ENABLE, MALI200_REG_VAL_PERF_CNT_ENABLE, mali_perf_cnt_enable_reset_value); + } + +#ifdef CONFIG_MALI400_HEATMAPS_ENABLED + if(job->uargs.perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_HEATMAP_ENABLE) { + mali_hw_core_register_write_relaxed(&core->hw_core, MALI200_REG_ADDR_MGMT_PERFMON_CONTR, ((job->uargs.tilesx & 0x3FF) << 16) | 1); + mali_hw_core_register_write_relaxed(&core->hw_core, MALI200_REG_ADDR_MGMT_PERFMON_BASE, job->uargs.heatmap_mem & 0xFFFFFFF8); + } +#endif /* CONFIG_MALI400_HEATMAPS_ENABLED */ + + MALI_DEBUG_PRINT(3, ("Mali PP: Starting job 0x%08X part %u/%u on PP core %s\n", job, sub_job + 1, mali_pp_job_get_sub_job_count(job), core->hw_core.description)); + + /* Adding barrier to make sure all rester writes are finished */ + _mali_osk_write_mem_barrier(); + + /* This is the command that starts the core. */ + mali_hw_core_register_write_relaxed(&core->hw_core, MALI200_REG_ADDR_MGMT_CTRL_MGMT, MALI200_REG_VAL_CTRL_MGMT_START_RENDERING); + + /* Adding barrier to make sure previous rester writes is finished */ + _mali_osk_write_mem_barrier(); +} + +u32 mali_pp_core_get_version(struct mali_pp_core *core) +{ + MALI_DEBUG_ASSERT_POINTER(core); + return mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_VERSION); +} + +struct mali_pp_core* mali_pp_get_global_pp_core(u32 index) +{ + if (mali_global_num_pp_cores > index) { + return mali_global_pp_cores[index]; + } + + return NULL; +} + +u32 mali_pp_get_glob_num_pp_cores(void) +{ + return mali_global_num_pp_cores; +} + +/* ------------- interrupt handling below ------------------ */ +static void mali_pp_irq_probe_trigger(void *data) +{ + struct mali_pp_core *core = (struct mali_pp_core *)data; + mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_MASK, MALI200_REG_VAL_IRQ_MASK_USED); + mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_RAWSTAT, MALI200_REG_VAL_IRQ_FORCE_HANG); + _mali_osk_mem_barrier(); +} + +static _mali_osk_errcode_t mali_pp_irq_probe_ack(void *data) +{ + struct mali_pp_core *core = (struct mali_pp_core *)data; + u32 irq_readout; + + irq_readout = mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_STATUS); + if (MALI200_REG_VAL_IRQ_FORCE_HANG & irq_readout) { + mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_CLEAR, MALI200_REG_VAL_IRQ_FORCE_HANG); + _mali_osk_mem_barrier(); + return _MALI_OSK_ERR_OK; + } + + return _MALI_OSK_ERR_FAULT; +} + + +#if 0 +static void mali_pp_print_registers(struct mali_pp_core *core) +{ + MALI_DEBUG_PRINT(2, ("Mali PP: Register MALI200_REG_ADDR_MGMT_VERSION = 0x%08X\n", mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_VERSION))); + MALI_DEBUG_PRINT(2, ("Mali PP: Register MALI200_REG_ADDR_MGMT_CURRENT_REND_LIST_ADDR = 0x%08X\n", mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_CURRENT_REND_LIST_ADDR))); + MALI_DEBUG_PRINT(2, ("Mali PP: Register MALI200_REG_ADDR_MGMT_STATUS = 0x%08X\n", mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_STATUS))); + MALI_DEBUG_PRINT(2, ("Mali PP: Register MALI200_REG_ADDR_MGMT_INT_RAWSTAT = 0x%08X\n", mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_RAWSTAT))); + MALI_DEBUG_PRINT(2, ("Mali PP: Register MALI200_REG_ADDR_MGMT_INT_MASK = 0x%08X\n", mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_MASK))); + MALI_DEBUG_PRINT(2, ("Mali PP: Register MALI200_REG_ADDR_MGMT_INT_STATUS = 0x%08X\n", mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_STATUS))); + MALI_DEBUG_PRINT(2, ("Mali PP: Register MALI200_REG_ADDR_MGMT_BUS_ERROR_STATUS = 0x%08X\n", mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_BUS_ERROR_STATUS))); + MALI_DEBUG_PRINT(2, ("Mali PP: Register MALI200_REG_ADDR_MGMT_PERF_CNT_0_ENABLE = 0x%08X\n", mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_0_ENABLE))); + MALI_DEBUG_PRINT(2, ("Mali PP: Register MALI200_REG_ADDR_MGMT_PERF_CNT_0_SRC = 0x%08X\n", mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_0_SRC))); + MALI_DEBUG_PRINT(2, ("Mali PP: Register MALI200_REG_ADDR_MGMT_PERF_CNT_0_VALUE = 0x%08X\n", mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_0_VALUE))); + MALI_DEBUG_PRINT(2, ("Mali PP: Register MALI200_REG_ADDR_MGMT_PERF_CNT_1_ENABLE = 0x%08X\n", mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_1_ENABLE))); + MALI_DEBUG_PRINT(2, ("Mali PP: Register MALI200_REG_ADDR_MGMT_PERF_CNT_1_SRC = 0x%08X\n", mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_1_SRC))); + MALI_DEBUG_PRINT(2, ("Mali PP: Register MALI200_REG_ADDR_MGMT_PERF_CNT_1_VALUE = 0x%08X\n", mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_1_VALUE))); +} +#endif + +#if 0 +void mali_pp_print_state(struct mali_pp_core *core) +{ + MALI_DEBUG_PRINT(2, ("Mali PP: State: 0x%08x\n", mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_STATUS) )); +} +#endif + +void mali_pp_update_performance_counters(struct mali_pp_core *parent, struct mali_pp_core *child, struct mali_pp_job *job, u32 subjob) +{ + u32 val0 = 0; + u32 val1 = 0; + u32 counter_src0 = mali_pp_job_get_perf_counter_src0(job, subjob); + u32 counter_src1 = mali_pp_job_get_perf_counter_src1(job, subjob); +#if defined(CONFIG_MALI400_PROFILING) + int counter_index = COUNTER_FP_0_C0 + (2 * child->core_id); +#endif + + if (MALI_HW_CORE_NO_COUNTER != counter_src0) { + val0 = mali_hw_core_register_read(&child->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_0_VALUE); + mali_pp_job_set_perf_counter_value0(job, subjob, val0); + +#if defined(CONFIG_MALI400_PROFILING) + _mali_osk_profiling_report_hw_counter(counter_index, val0); +#endif + } + + if (MALI_HW_CORE_NO_COUNTER != counter_src1) { + val1 = mali_hw_core_register_read(&child->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_1_VALUE); + mali_pp_job_set_perf_counter_value1(job, subjob, val1); + +#if defined(CONFIG_MALI400_PROFILING) + _mali_osk_profiling_report_hw_counter(counter_index + 1, val1); +#endif + } +} + +#if MALI_STATE_TRACKING +u32 mali_pp_dump_state(struct mali_pp_core *core, char *buf, u32 size) +{ + int n = 0; + + n += _mali_osk_snprintf(buf + n, size - n, "\tPP #%d: %s\n", core->core_id, core->hw_core.description); + + return n; +} +#endif diff --git a/drivers/gpu/arm/mali/common/mali_pp.h b/drivers/gpu/arm/mali/common/mali_pp.h new file mode 100644 index 00000000000000..a7d595671d1f5b --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_pp.h @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2011-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_PP_H__ +#define __MALI_PP_H__ + +#include "mali_osk.h" +#include "mali_pp_job.h" +#include "mali_hw_core.h" +#include "mali_dma.h" + +struct mali_group; + +#define MALI_MAX_NUMBER_OF_PP_CORES 9 + +/** + * Definition of the PP core struct + * Used to track a PP core in the system. + */ +struct mali_pp_core { + struct mali_hw_core hw_core; /**< Common for all HW cores */ + _mali_osk_irq_t *irq; /**< IRQ handler */ + u32 core_id; /**< Unique core ID */ + u32 bcast_id; /**< The "flag" value used by the Mali-450 broadcast and DLBU unit */ +}; + +_mali_osk_errcode_t mali_pp_initialize(void); +void mali_pp_terminate(void); + +struct mali_pp_core *mali_pp_create(const _mali_osk_resource_t * resource, struct mali_group *group, mali_bool is_virtual, u32 bcast_id); +void mali_pp_delete(struct mali_pp_core *core); + +void mali_pp_stop_bus(struct mali_pp_core *core); +_mali_osk_errcode_t mali_pp_stop_bus_wait(struct mali_pp_core *core); +void mali_pp_reset_async(struct mali_pp_core *core); +_mali_osk_errcode_t mali_pp_reset_wait(struct mali_pp_core *core); +_mali_osk_errcode_t mali_pp_reset(struct mali_pp_core *core); +_mali_osk_errcode_t mali_pp_hard_reset(struct mali_pp_core *core); + +void mali_pp_job_start(struct mali_pp_core *core, struct mali_pp_job *job, u32 sub_job, mali_bool restart_virtual); + +/** + * @brief Add commands to DMA command buffer to start PP job on core. + */ +void mali_pp_job_dma_cmd_prepare(struct mali_pp_core *core, struct mali_pp_job *job, u32 sub_job, + mali_bool restart_virtual, mali_dma_cmd_buf *buf); + +u32 mali_pp_core_get_version(struct mali_pp_core *core); + +MALI_STATIC_INLINE u32 mali_pp_core_get_id(struct mali_pp_core *core) +{ + MALI_DEBUG_ASSERT_POINTER(core); + return core->core_id; +} + +MALI_STATIC_INLINE u32 mali_pp_core_get_bcast_id(struct mali_pp_core *core) +{ + MALI_DEBUG_ASSERT_POINTER(core); + return core->bcast_id; +} + +struct mali_pp_core* mali_pp_get_global_pp_core(u32 index); +u32 mali_pp_get_glob_num_pp_cores(void); + +/* Debug */ +u32 mali_pp_dump_state(struct mali_pp_core *core, char *buf, u32 size); + +/** + * Put instrumented HW counters from the core(s) to the job object (if enabled) + * + * parent and child is always the same, except for virtual jobs on Mali-450. + * In this case, the counters will be enabled on the virtual core (parent), + * but values need to be read from the child cores. + * + * @param parent The core used to see if the counters was enabled + * @param child The core to actually read the values from + * @job Job object to update with counter values (if enabled) + * @subjob Which subjob the counters are applicable for (core ID for virtual jobs) + */ +void mali_pp_update_performance_counters(struct mali_pp_core *parent, struct mali_pp_core *child, struct mali_pp_job *job, u32 subjob); + +MALI_STATIC_INLINE const char *mali_pp_get_hw_core_desc(struct mali_pp_core *core) +{ + return core->hw_core.description; +} + +/*** Register reading/writing functions ***/ +MALI_STATIC_INLINE u32 mali_pp_get_int_stat(struct mali_pp_core *core) +{ + return mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_STATUS); +} + +MALI_STATIC_INLINE u32 mali_pp_read_rawstat(struct mali_pp_core *core) +{ + return mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_RAWSTAT) & MALI200_REG_VAL_IRQ_MASK_USED; +} + +MALI_STATIC_INLINE u32 mali_pp_read_status(struct mali_pp_core *core) +{ + return mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_STATUS); +} + +MALI_STATIC_INLINE void mali_pp_mask_all_interrupts(struct mali_pp_core *core) +{ + mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_MASK, MALI200_REG_VAL_IRQ_MASK_NONE); +} + +MALI_STATIC_INLINE void mali_pp_clear_hang_interrupt(struct mali_pp_core *core) +{ + mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_CLEAR, MALI200_REG_VAL_IRQ_HANG); +} + +MALI_STATIC_INLINE void mali_pp_enable_interrupts(struct mali_pp_core *core) +{ + mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_MASK, MALI200_REG_VAL_IRQ_MASK_USED); +} + +MALI_STATIC_INLINE void mali_pp_write_addr_stack(struct mali_pp_core *core, struct mali_pp_job *job) +{ + u32 addr = mali_pp_job_get_addr_stack(job, core->core_id); + mali_hw_core_register_write_relaxed(&core->hw_core, MALI200_REG_ADDR_STACK, addr); +} + +#endif /* __MALI_PP_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_pp_job.c b/drivers/gpu/arm/mali/common/mali_pp_job.c new file mode 100644 index 00000000000000..af9cb54cfbfee8 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_pp_job.c @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2011-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_pp.h" +#include "mali_pp_job.h" +#include "mali_dma.h" +#include "mali_osk.h" +#include "mali_osk_list.h" +#include "mali_kernel_common.h" +#include "mali_uk_types.h" +#include "mali_pp_scheduler.h" +#if defined(CONFIG_DMA_SHARED_BUFFER) && !defined(CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH) +#include "linux/mali_memory_dma_buf.h" +#endif + +static u32 pp_counter_src0 = MALI_HW_CORE_NO_COUNTER; /**< Performance counter 0, MALI_HW_CORE_NO_COUNTER for disabled */ +static u32 pp_counter_src1 = MALI_HW_CORE_NO_COUNTER; /**< Performance counter 1, MALI_HW_CORE_NO_COUNTER for disabled */ +static _mali_osk_atomic_t pp_counter_per_sub_job_count; /**< Number of values in the two arrays which is != MALI_HW_CORE_NO_COUNTER */ +static u32 pp_counter_per_sub_job_src0[_MALI_PP_MAX_SUB_JOBS] = { MALI_HW_CORE_NO_COUNTER, MALI_HW_CORE_NO_COUNTER, MALI_HW_CORE_NO_COUNTER, MALI_HW_CORE_NO_COUNTER, MALI_HW_CORE_NO_COUNTER, MALI_HW_CORE_NO_COUNTER, MALI_HW_CORE_NO_COUNTER, MALI_HW_CORE_NO_COUNTER }; +static u32 pp_counter_per_sub_job_src1[_MALI_PP_MAX_SUB_JOBS] = { MALI_HW_CORE_NO_COUNTER, MALI_HW_CORE_NO_COUNTER, MALI_HW_CORE_NO_COUNTER, MALI_HW_CORE_NO_COUNTER, MALI_HW_CORE_NO_COUNTER, MALI_HW_CORE_NO_COUNTER, MALI_HW_CORE_NO_COUNTER, MALI_HW_CORE_NO_COUNTER }; + +void mali_pp_job_initialize(void) +{ + _mali_osk_atomic_init(&pp_counter_per_sub_job_count, 0); +} + +void mali_pp_job_terminate(void) +{ + _mali_osk_atomic_term(&pp_counter_per_sub_job_count); +} + +struct mali_pp_job *mali_pp_job_create(struct mali_session_data *session, _mali_uk_pp_start_job_s *uargs, u32 id) +{ + struct mali_pp_job *job; + u32 perf_counter_flag; + + job = _mali_osk_calloc(1, sizeof(struct mali_pp_job)); + if (NULL != job) { + if (0 != _mali_osk_copy_from_user(&job->uargs, uargs, sizeof(_mali_uk_pp_start_job_s))) { + goto fail; + } + + if (job->uargs.num_cores > _MALI_PP_MAX_SUB_JOBS) { + MALI_PRINT_ERROR(("Mali PP job: Too many sub jobs specified in job object\n")); + goto fail; + } + + if (!mali_pp_job_use_no_notification(job)) { + job->finished_notification = _mali_osk_notification_create(_MALI_NOTIFICATION_PP_FINISHED, sizeof(_mali_uk_pp_job_finished_s)); + if (NULL == job->finished_notification) goto fail; + } + + perf_counter_flag = mali_pp_job_get_perf_counter_flag(job); + + /* case when no counters came from user space + * so pass the debugfs / DS-5 provided global ones to the job object */ + if (!((perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_SRC0_ENABLE) || + (perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_SRC1_ENABLE))) { + u32 sub_job_count = _mali_osk_atomic_read(&pp_counter_per_sub_job_count); + + /* These counters apply for all virtual jobs, and where no per sub job counter is specified */ + job->uargs.perf_counter_src0 = pp_counter_src0; + job->uargs.perf_counter_src1 = pp_counter_src1; + + /* We only copy the per sub job array if it is enabled with at least one counter */ + if (0 < sub_job_count) { + job->perf_counter_per_sub_job_count = sub_job_count; + _mali_osk_memcpy(job->perf_counter_per_sub_job_src0, pp_counter_per_sub_job_src0, sizeof(pp_counter_per_sub_job_src0)); + _mali_osk_memcpy(job->perf_counter_per_sub_job_src1, pp_counter_per_sub_job_src1, sizeof(pp_counter_per_sub_job_src1)); + } + } + + _mali_osk_list_init(&job->list); + job->session = session; + _mali_osk_list_init(&job->session_list); + job->id = id; + + job->sub_jobs_num = job->uargs.num_cores ? job->uargs.num_cores : 1; + job->pid = _mali_osk_get_pid(); + job->tid = _mali_osk_get_tid(); + + job->num_memory_cookies = job->uargs.num_memory_cookies; + if (job->num_memory_cookies > 0) { + u32 size; + + if (job->uargs.num_memory_cookies > session->descriptor_mapping->current_nr_mappings) { + MALI_PRINT_ERROR(("Mali PP job: Too many memory cookies specified in job object\n")); + goto fail; + } + + size = sizeof(*job->uargs.memory_cookies) * job->num_memory_cookies; + + job->memory_cookies = _mali_osk_malloc(size); + if (NULL == job->memory_cookies) { + MALI_PRINT_ERROR(("Mali PP job: Failed to allocate %d bytes of memory cookies!\n", size)); + goto fail; + } + + if (0 != _mali_osk_copy_from_user(job->memory_cookies, job->uargs.memory_cookies, size)) { + MALI_PRINT_ERROR(("Mali PP job: Failed to copy %d bytes of memory cookies from user!\n", size)); + goto fail; + } + +#if defined(CONFIG_DMA_SHARED_BUFFER) && !defined(CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH) + job->num_dma_bufs = job->num_memory_cookies; + job->dma_bufs = _mali_osk_calloc(job->num_dma_bufs, sizeof(struct mali_dma_buf_attachment *)); + if (NULL == job->dma_bufs) { + MALI_PRINT_ERROR(("Mali PP job: Failed to allocate dma_bufs array!\n")); + goto fail; + } +#endif + } + + /* Prepare DMA command buffer to start job, if it is virtual. */ + if (mali_pp_job_is_virtual(job)) { + struct mali_pp_core *core; + _mali_osk_errcode_t err = mali_dma_get_cmd_buf(&job->dma_cmd_buf); + + if (_MALI_OSK_ERR_OK != err) { + MALI_PRINT_ERROR(("Mali PP job: Failed to allocate DMA command buffer\n")); + goto fail; + } + + core = mali_pp_scheduler_get_virtual_pp(); + MALI_DEBUG_ASSERT_POINTER(core); + + mali_pp_job_dma_cmd_prepare(core, job, 0, MALI_FALSE, &job->dma_cmd_buf); + } + + if (_MALI_OSK_ERR_OK != mali_pp_job_check(job)) { + /* Not a valid job. */ + goto fail; + } + + mali_timeline_tracker_init(&job->tracker, MALI_TIMELINE_TRACKER_PP, NULL, job); + mali_timeline_fence_copy_uk_fence(&(job->tracker.fence), &(job->uargs.fence)); + + return job; + } + +fail: + if (NULL != job) { + mali_pp_job_delete(job); + } + + return NULL; +} + +void mali_pp_job_delete(struct mali_pp_job *job) +{ + mali_dma_put_cmd_buf(&job->dma_cmd_buf); + if (NULL != job->finished_notification) { + _mali_osk_notification_delete(job->finished_notification); + } + + _mali_osk_free(job->memory_cookies); + +#if defined(CONFIG_DMA_SHARED_BUFFER) && !defined(CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH) + /* Unmap buffers attached to job */ + if (0 < job->num_dma_bufs) { + mali_dma_buf_unmap_job(job); + } + + _mali_osk_free(job->dma_bufs); +#endif /* CONFIG_DMA_SHARED_BUFFER */ + + _mali_osk_free(job); +} + +u32 mali_pp_job_get_perf_counter_src0(struct mali_pp_job *job, u32 sub_job) +{ + /* Virtual jobs always use the global job counter (or if there are per sub job counters at all) */ + if (mali_pp_job_is_virtual(job) || 0 == job->perf_counter_per_sub_job_count) { + return job->uargs.perf_counter_src0; + } + + /* Use per sub job counter if enabled... */ + if (MALI_HW_CORE_NO_COUNTER != job->perf_counter_per_sub_job_src0[sub_job]) { + return job->perf_counter_per_sub_job_src0[sub_job]; + } + + /* ...else default to global job counter */ + return job->uargs.perf_counter_src0; +} + +u32 mali_pp_job_get_perf_counter_src1(struct mali_pp_job *job, u32 sub_job) +{ + /* Virtual jobs always use the global job counter (or if there are per sub job counters at all) */ + if (mali_pp_job_is_virtual(job) || 0 == job->perf_counter_per_sub_job_count) { + /* Virtual jobs always use the global job counter */ + return job->uargs.perf_counter_src1; + } + + /* Use per sub job counter if enabled... */ + if (MALI_HW_CORE_NO_COUNTER != job->perf_counter_per_sub_job_src1[sub_job]) { + return job->perf_counter_per_sub_job_src1[sub_job]; + } + + /* ...else default to global job counter */ + return job->uargs.perf_counter_src1; +} + +void mali_pp_job_set_pp_counter_global_src0(u32 counter) +{ + pp_counter_src0 = counter; +} + +void mali_pp_job_set_pp_counter_global_src1(u32 counter) +{ + pp_counter_src1 = counter; +} + +void mali_pp_job_set_pp_counter_sub_job_src0(u32 sub_job, u32 counter) +{ + MALI_DEBUG_ASSERT(sub_job < _MALI_PP_MAX_SUB_JOBS); + + if (MALI_HW_CORE_NO_COUNTER == pp_counter_per_sub_job_src0[sub_job]) { + /* increment count since existing counter was disabled */ + _mali_osk_atomic_inc(&pp_counter_per_sub_job_count); + } + + if (MALI_HW_CORE_NO_COUNTER == counter) { + /* decrement count since new counter is disabled */ + _mali_osk_atomic_dec(&pp_counter_per_sub_job_count); + } + + /* PS: A change from MALI_HW_CORE_NO_COUNTER to MALI_HW_CORE_NO_COUNTER will inc and dec, result will be 0 change */ + + pp_counter_per_sub_job_src0[sub_job] = counter; +} + +void mali_pp_job_set_pp_counter_sub_job_src1(u32 sub_job, u32 counter) +{ + MALI_DEBUG_ASSERT(sub_job < _MALI_PP_MAX_SUB_JOBS); + + if (MALI_HW_CORE_NO_COUNTER == pp_counter_per_sub_job_src1[sub_job]) { + /* increment count since existing counter was disabled */ + _mali_osk_atomic_inc(&pp_counter_per_sub_job_count); + } + + if (MALI_HW_CORE_NO_COUNTER == counter) { + /* decrement count since new counter is disabled */ + _mali_osk_atomic_dec(&pp_counter_per_sub_job_count); + } + + /* PS: A change from MALI_HW_CORE_NO_COUNTER to MALI_HW_CORE_NO_COUNTER will inc and dec, result will be 0 change */ + + pp_counter_per_sub_job_src1[sub_job] = counter; +} + +u32 mali_pp_job_get_pp_counter_global_src0(void) +{ + return pp_counter_src0; +} + +u32 mali_pp_job_get_pp_counter_global_src1(void) +{ + return pp_counter_src1; +} + +u32 mali_pp_job_get_pp_counter_sub_job_src0(u32 sub_job) +{ + MALI_DEBUG_ASSERT(sub_job < _MALI_PP_MAX_SUB_JOBS); + return pp_counter_per_sub_job_src0[sub_job]; +} + +u32 mali_pp_job_get_pp_counter_sub_job_src1(u32 sub_job) +{ + MALI_DEBUG_ASSERT(sub_job < _MALI_PP_MAX_SUB_JOBS); + return pp_counter_per_sub_job_src1[sub_job]; +} diff --git a/drivers/gpu/arm/mali/common/mali_pp_job.h b/drivers/gpu/arm/mali/common/mali_pp_job.h new file mode 100644 index 00000000000000..17317b4dd9aa90 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_pp_job.h @@ -0,0 +1,384 @@ +/* + * Copyright (C) 2011-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_PP_JOB_H__ +#define __MALI_PP_JOB_H__ + +#include "mali_osk.h" +#include "mali_osk_list.h" +#include "mali_uk_types.h" +#include "mali_session.h" +#include "mali_kernel_common.h" +#include "regs/mali_200_regs.h" +#include "mali_kernel_core.h" +#include "mali_dma.h" +#include "mali_dlbu.h" +#include "mali_timeline.h" +#if defined(CONFIG_DMA_SHARED_BUFFER) && !defined(CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH) +#include "linux/mali_memory_dma_buf.h" +#endif + +/** + * The structure represents a PP job, including all sub-jobs + * (This struct unfortunately needs to be public because of how the _mali_osk_list_* + * mechanism works) + */ +struct mali_pp_job { + _mali_osk_list_t list; /**< Used to link jobs together in the scheduler queue */ + struct mali_session_data *session; /**< Session which submitted this job */ + _mali_osk_list_t session_list; /**< Used to link jobs together in the session job list */ + _mali_osk_list_t session_fb_lookup_list; /**< Used to link jobs together from the same frame builder in the session */ + _mali_uk_pp_start_job_s uargs; /**< Arguments from user space */ + mali_dma_cmd_buf dma_cmd_buf; /**< Command buffer for starting job using Mali-450 DMA unit */ + u32 id; /**< Identifier for this job in kernel space (sequential numbering) */ + u32 cache_order; /**< Cache order used for L2 cache flushing (sequential numbering) */ + u32 perf_counter_value0[_MALI_PP_MAX_SUB_JOBS]; /**< Value of performance counter 0 (to be returned to user space), one for each sub job */ + u32 perf_counter_value1[_MALI_PP_MAX_SUB_JOBS]; /**< Value of performance counter 1 (to be returned to user space), one for each sub job */ + u32 sub_jobs_num; /**< Number of subjobs; set to 1 for Mali-450 if DLBU is used, otherwise equals number of PP cores */ + u32 sub_jobs_started; /**< Total number of sub-jobs started (always started in ascending order) */ + u32 sub_jobs_completed; /**< Number of completed sub-jobs in this superjob */ + u32 sub_job_errors; /**< Bitfield with errors (errors for each single sub-job is or'ed together) */ + u32 pid; /**< Process ID of submitting process */ + u32 tid; /**< Thread ID of submitting thread */ + _mali_osk_notification_t *finished_notification; /**< Notification sent back to userspace on job complete */ + u32 num_memory_cookies; /**< Number of memory cookies attached to job */ + u32 *memory_cookies; /**< Memory cookies attached to job */ +#if defined(CONFIG_DMA_SHARED_BUFFER) && !defined(CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH) + struct mali_dma_buf_attachment **dma_bufs; /**< Array of DMA-bufs used by job */ + u32 num_dma_bufs; /**< Number of DMA-bufs used by job */ +#endif + struct mali_timeline_tracker tracker; /**< Timeline tracker for this job */ + u32 perf_counter_per_sub_job_count; /**< Number of values in the two arrays which is != MALI_HW_CORE_NO_COUNTER */ + u32 perf_counter_per_sub_job_src0[_MALI_PP_MAX_SUB_JOBS]; /**< Per sub job counters src0 */ + u32 perf_counter_per_sub_job_src1[_MALI_PP_MAX_SUB_JOBS]; /**< Per sub job counters src1 */ +}; + +void mali_pp_job_initialize(void); +void mali_pp_job_terminate(void); + +struct mali_pp_job *mali_pp_job_create(struct mali_session_data *session, _mali_uk_pp_start_job_s *uargs, u32 id); +void mali_pp_job_delete(struct mali_pp_job *job); + +u32 mali_pp_job_get_perf_counter_src0(struct mali_pp_job *job, u32 sub_job); +u32 mali_pp_job_get_perf_counter_src1(struct mali_pp_job *job, u32 sub_job); + +void mali_pp_job_set_pp_counter_global_src0(u32 counter); +void mali_pp_job_set_pp_counter_global_src1(u32 counter); +void mali_pp_job_set_pp_counter_sub_job_src0(u32 sub_job, u32 counter); +void mali_pp_job_set_pp_counter_sub_job_src1(u32 sub_job, u32 counter); + +u32 mali_pp_job_get_pp_counter_global_src0(void); +u32 mali_pp_job_get_pp_counter_global_src1(void); +u32 mali_pp_job_get_pp_counter_sub_job_src0(u32 sub_job); +u32 mali_pp_job_get_pp_counter_sub_job_src1(u32 sub_job); + +MALI_STATIC_INLINE u32 mali_pp_job_get_id(struct mali_pp_job *job) +{ + return (NULL == job) ? 0 : job->id; +} + +MALI_STATIC_INLINE u32 mali_pp_job_get_cache_order(struct mali_pp_job *job) +{ + return (NULL == job) ? 0 : job->cache_order; +} + +MALI_STATIC_INLINE u32 mali_pp_job_get_user_id(struct mali_pp_job *job) +{ + return job->uargs.user_job_ptr; +} + +MALI_STATIC_INLINE u32 mali_pp_job_get_frame_builder_id(struct mali_pp_job *job) +{ + return job->uargs.frame_builder_id; +} + +MALI_STATIC_INLINE u32 mali_pp_job_get_flush_id(struct mali_pp_job *job) +{ + return job->uargs.flush_id; +} + +MALI_STATIC_INLINE u32 mali_pp_job_get_pid(struct mali_pp_job *job) +{ + return job->pid; +} + +MALI_STATIC_INLINE u32 mali_pp_job_get_tid(struct mali_pp_job *job) +{ + return job->tid; +} + +MALI_STATIC_INLINE u32* mali_pp_job_get_frame_registers(struct mali_pp_job *job) +{ + return job->uargs.frame_registers; +} + +MALI_STATIC_INLINE u32* mali_pp_job_get_dlbu_registers(struct mali_pp_job *job) +{ + return job->uargs.dlbu_registers; +} + +MALI_STATIC_INLINE mali_bool mali_pp_job_is_virtual(struct mali_pp_job *job) +{ +#if defined(CONFIG_MALI450) + return 0 == job->uargs.num_cores; +#else + return MALI_FALSE; +#endif +} + +MALI_STATIC_INLINE u32 mali_pp_job_get_addr_frame(struct mali_pp_job *job, u32 sub_job) +{ + if (mali_pp_job_is_virtual(job)) { + return MALI_DLBU_VIRT_ADDR; + } else if (0 == sub_job) { + return job->uargs.frame_registers[MALI200_REG_ADDR_FRAME / sizeof(u32)]; + } else if (sub_job < _MALI_PP_MAX_SUB_JOBS) { + return job->uargs.frame_registers_addr_frame[sub_job - 1]; + } + + return 0; +} + +MALI_STATIC_INLINE u32 mali_pp_job_get_addr_stack(struct mali_pp_job *job, u32 sub_job) +{ + if (0 == sub_job) { + return job->uargs.frame_registers[MALI200_REG_ADDR_STACK / sizeof(u32)]; + } else if (sub_job < _MALI_PP_MAX_SUB_JOBS) { + return job->uargs.frame_registers_addr_stack[sub_job - 1]; + } + + return 0; +} + +MALI_STATIC_INLINE u32* mali_pp_job_get_wb0_registers(struct mali_pp_job *job) +{ + return job->uargs.wb0_registers; +} + +MALI_STATIC_INLINE u32* mali_pp_job_get_wb1_registers(struct mali_pp_job *job) +{ + return job->uargs.wb1_registers; +} + +MALI_STATIC_INLINE u32* mali_pp_job_get_wb2_registers(struct mali_pp_job *job) +{ + return job->uargs.wb2_registers; +} + +MALI_STATIC_INLINE void mali_pp_job_disable_wb0(struct mali_pp_job *job) +{ + job->uargs.wb0_registers[MALI200_REG_ADDR_WB_SOURCE_SELECT] = 0; +} + +MALI_STATIC_INLINE void mali_pp_job_disable_wb1(struct mali_pp_job *job) +{ + job->uargs.wb1_registers[MALI200_REG_ADDR_WB_SOURCE_SELECT] = 0; +} + +MALI_STATIC_INLINE void mali_pp_job_disable_wb2(struct mali_pp_job *job) +{ + job->uargs.wb2_registers[MALI200_REG_ADDR_WB_SOURCE_SELECT] = 0; +} + +MALI_STATIC_INLINE mali_bool mali_pp_job_all_writeback_unit_disabled(struct mali_pp_job *job) +{ + MALI_DEBUG_ASSERT_POINTER(job); + + if ( job->uargs.wb0_registers[MALI200_REG_ADDR_WB_SOURCE_SELECT] || + job->uargs.wb1_registers[MALI200_REG_ADDR_WB_SOURCE_SELECT] || + job->uargs.wb2_registers[MALI200_REG_ADDR_WB_SOURCE_SELECT] + ) { + /* At least one output unit active */ + return MALI_FALSE; + } + + /* All outputs are disabled - we can abort the job */ + return MALI_TRUE; +} + +MALI_STATIC_INLINE u32 mali_pp_job_get_fb_lookup_id(struct mali_pp_job *job) +{ + MALI_DEBUG_ASSERT_POINTER(job); + + return MALI_PP_JOB_FB_LOOKUP_LIST_MASK & job->uargs.frame_builder_id; +} + +MALI_STATIC_INLINE struct mali_session_data *mali_pp_job_get_session(struct mali_pp_job *job) +{ + return job->session; +} + +MALI_STATIC_INLINE mali_bool mali_pp_job_has_unstarted_sub_jobs(struct mali_pp_job *job) +{ + return (job->sub_jobs_started < job->sub_jobs_num) ? MALI_TRUE : MALI_FALSE; +} + +/* Function used when we are terminating a session with jobs. Return TRUE if it has a rendering job. + Makes sure that no new subjobs are started. */ +MALI_STATIC_INLINE void mali_pp_job_mark_unstarted_failed(struct mali_pp_job *job) +{ + u32 jobs_remaining = job->sub_jobs_num - job->sub_jobs_started; + job->sub_jobs_started += jobs_remaining; + job->sub_jobs_completed += jobs_remaining; + job->sub_job_errors += jobs_remaining; +} + +MALI_STATIC_INLINE void mali_pp_job_mark_unstarted_success(struct mali_pp_job *job) +{ + u32 jobs_remaining = job->sub_jobs_num - job->sub_jobs_started; + job->sub_jobs_started += jobs_remaining; + job->sub_jobs_completed += jobs_remaining; +} + +MALI_STATIC_INLINE mali_bool mali_pp_job_is_complete(struct mali_pp_job *job) +{ + return (job->sub_jobs_num == job->sub_jobs_completed) ? MALI_TRUE : MALI_FALSE; +} + +MALI_STATIC_INLINE u32 mali_pp_job_get_first_unstarted_sub_job(struct mali_pp_job *job) +{ + return job->sub_jobs_started; +} + +MALI_STATIC_INLINE u32 mali_pp_job_get_sub_job_count(struct mali_pp_job *job) +{ + return job->sub_jobs_num; +} + +MALI_STATIC_INLINE mali_bool mali_pp_job_needs_dma_buf_mapping(struct mali_pp_job *job) +{ + MALI_DEBUG_ASSERT(job); + + if (0 != job->num_memory_cookies) { + return MALI_TRUE; + } + + return MALI_FALSE; +} + +MALI_STATIC_INLINE void mali_pp_job_mark_sub_job_started(struct mali_pp_job *job, u32 sub_job) +{ + MALI_DEBUG_ASSERT_POINTER(job); + + /* Assert that we are marking the "first unstarted sub job" as started */ + MALI_DEBUG_ASSERT(job->sub_jobs_started == sub_job); + + job->sub_jobs_started++; +} + +MALI_STATIC_INLINE void mali_pp_job_mark_sub_job_completed(struct mali_pp_job *job, mali_bool success) +{ + job->sub_jobs_completed++; + if ( MALI_FALSE == success ) { + job->sub_job_errors++; + } +} + +MALI_STATIC_INLINE mali_bool mali_pp_job_was_success(struct mali_pp_job *job) +{ + if ( 0 == job->sub_job_errors ) { + return MALI_TRUE; + } + return MALI_FALSE; +} + +MALI_STATIC_INLINE mali_bool mali_pp_job_use_no_notification(struct mali_pp_job *job) +{ + return job->uargs.flags & _MALI_PP_JOB_FLAG_NO_NOTIFICATION ? MALI_TRUE : MALI_FALSE; +} + +MALI_STATIC_INLINE u32 mali_pp_job_get_perf_counter_flag(struct mali_pp_job *job) +{ + return job->uargs.perf_counter_flag; +} + + +MALI_STATIC_INLINE u32 mali_pp_job_get_perf_counter_value0(struct mali_pp_job *job, u32 sub_job) +{ + return job->perf_counter_value0[sub_job]; +} + +MALI_STATIC_INLINE u32 mali_pp_job_get_perf_counter_value1(struct mali_pp_job *job, u32 sub_job) +{ + return job->perf_counter_value1[sub_job]; +} + +MALI_STATIC_INLINE void mali_pp_job_set_perf_counter_value0(struct mali_pp_job *job, u32 sub_job, u32 value) +{ + job->perf_counter_value0[sub_job] = value; +} + +MALI_STATIC_INLINE void mali_pp_job_set_perf_counter_value1(struct mali_pp_job *job, u32 sub_job, u32 value) +{ + job->perf_counter_value1[sub_job] = value; +} + +MALI_STATIC_INLINE _mali_osk_errcode_t mali_pp_job_check(struct mali_pp_job *job) +{ + if (mali_pp_job_is_virtual(job) && job->sub_jobs_num != 1) { + return _MALI_OSK_ERR_FAULT; + } + return _MALI_OSK_ERR_OK; +} + +/** + * Returns MALI_TRUE if first job should be started after second job. + * + * @param first First job. + * @param second Second job. + * @return MALI_TRUE if first job should be started after second job, MALI_FALSE if not. + */ +MALI_STATIC_INLINE mali_bool mali_pp_job_should_start_after(struct mali_pp_job *first, struct mali_pp_job *second) +{ + MALI_DEBUG_ASSERT_POINTER(first); + MALI_DEBUG_ASSERT_POINTER(second); + + /* First job should be started after second job if second job is in progress. */ + if (0 < second->sub_jobs_started) { + return MALI_TRUE; + } + + /* First job should be started after second job if first job has a higher job id. A span is + used to handle job id wrapping. */ + if ((mali_pp_job_get_id(first) - mali_pp_job_get_id(second)) < MALI_SCHEDULER_JOB_ID_SPAN) { + return MALI_TRUE; + } + + /* Second job should be started after first job. */ + return MALI_FALSE; +} + +/** + * Returns MALI_TRUE if this job has more than two sub jobs and all sub jobs are unstarted. + * + * @param job Job to check. + * @return MALI_TRUE if job has more than two sub jobs and all sub jobs are unstarted, MALI_FALSE if not. + */ +MALI_STATIC_INLINE mali_bool mali_pp_job_is_large_and_unstarted(struct mali_pp_job *job) +{ + MALI_DEBUG_ASSERT_POINTER(job); + MALI_DEBUG_ASSERT(!mali_pp_job_is_virtual(job)); + + return (0 == job->sub_jobs_started && 2 < job->sub_jobs_num); +} + +/** + * Get PP job's Timeline tracker. + * + * @param job PP job. + * @return Pointer to Timeline tracker for the job. + */ +MALI_STATIC_INLINE struct mali_timeline_tracker *mali_pp_job_get_tracker(struct mali_pp_job *job) +{ + MALI_DEBUG_ASSERT_POINTER(job); + return &(job->tracker); +} + +#endif /* __MALI_PP_JOB_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_pp_scheduler.c b/drivers/gpu/arm/mali/common/mali_pp_scheduler.c new file mode 100644 index 00000000000000..8dd8e3e3988d61 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_pp_scheduler.c @@ -0,0 +1,2067 @@ +/* + * Copyright (C) 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_pp_scheduler.h" +#include "mali_kernel_common.h" +#include "mali_kernel_core.h" +#include "mali_osk.h" +#include "mali_osk_list.h" +#include "mali_scheduler.h" +#include "mali_pp.h" +#include "mali_pp_job.h" +#include "mali_group.h" +#include "mali_pm.h" +#include "mali_timeline.h" +#include "mali_osk_profiling.h" +#include "mali_kernel_utilization.h" +#include "mali_session.h" +#include "mali_pm_domain.h" +#include "linux/mali/mali_utgard.h" + +#if defined(CONFIG_DMA_SHARED_BUFFER) +#include "mali_memory_dma_buf.h" +#endif +#if defined(CONFIG_GPU_TRACEPOINTS) && defined(CONFIG_TRACEPOINTS) +#include +#include +#endif + +/* Queue type used for physical and virtual job queues. */ +struct mali_pp_scheduler_job_queue { + _MALI_OSK_LIST_HEAD(normal_pri); /* List of jobs with some unscheduled work. */ + _MALI_OSK_LIST_HEAD(high_pri); /* List of high priority jobs with some unscheduled work. */ + u32 depth; /* Depth of combined queues. */ +}; + +/* If dma_buf with map on demand is used, we defer job deletion and job queue if in atomic context, + * since both might sleep. */ +#if defined(CONFIG_DMA_SHARED_BUFFER) && !defined(CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH) +#define MALI_PP_SCHEDULER_USE_DEFERRED_JOB_DELETE 1 +#define MALI_PP_SCHEDULER_USE_DEFERRED_JOB_QUEUE 1 +#endif /* !defined(CONFIG_DMA_SHARED_BUFFER) && !defined(CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH) */ + +static void mali_pp_scheduler_job_queued(void); +static void mali_pp_scheduler_job_completed(void); + +/* Maximum of 8 PP cores (a group can only have maximum of 1 PP core) */ +#define MALI_MAX_NUMBER_OF_PP_GROUPS 9 + +static mali_bool mali_pp_scheduler_is_suspended(void *data); + +static u32 pp_version = 0; + +/* Physical job queue */ +static struct mali_pp_scheduler_job_queue job_queue; + +/* Physical groups */ +static _MALI_OSK_LIST_HEAD_STATIC_INIT(group_list_working); /* List of physical groups with working jobs on the pp core */ +static _MALI_OSK_LIST_HEAD_STATIC_INIT(group_list_idle); /* List of physical groups with idle jobs on the pp core */ +static _MALI_OSK_LIST_HEAD_STATIC_INIT(group_list_disabled); /* List of disabled physical groups */ + +/* Virtual job queue (Mali-450 only) */ +static struct mali_pp_scheduler_job_queue virtual_job_queue; + +/** + * Add job to scheduler queue. + * + * @param job Job to queue. + * @return Schedule mask. + */ +static mali_scheduler_mask mali_pp_scheduler_queue_job(struct mali_pp_job *job); + +/* Virtual group (Mali-450 only) */ +static struct mali_group *virtual_group = NULL; /* Virtual group (if any) */ +static enum { + VIRTUAL_GROUP_IDLE, + VIRTUAL_GROUP_WORKING, + VIRTUAL_GROUP_DISABLED, +} +virtual_group_state = VIRTUAL_GROUP_IDLE; /* Flag which indicates whether the virtual group is working or idle */ + +/* Number of physical cores */ +static u32 num_cores = 0; + +/* Number of physical cores which are enabled */ +static u32 enabled_cores = 0; + +/* Enable or disable core scaling */ +static mali_bool core_scaling_enabled = MALI_TRUE; + +/* Variables to allow safe pausing of the scheduler */ +static _mali_osk_wait_queue_t *pp_scheduler_working_wait_queue = NULL; +static u32 pause_count = 0; + +#if defined(MALI_UPPER_HALF_SCHEDULING) +static _mali_osk_spinlock_irq_t *pp_scheduler_lock = NULL; +#else +static _mali_osk_spinlock_t *pp_scheduler_lock = NULL; +#endif /* defined(MALI_UPPER_HALF_SCHEDULING) */ + +MALI_STATIC_INLINE void mali_pp_scheduler_lock(void) +{ +#if defined(MALI_UPPER_HALF_SCHEDULING) + _mali_osk_spinlock_irq_lock(pp_scheduler_lock); +#else + _mali_osk_spinlock_lock(pp_scheduler_lock); +#endif /* defined(MALI_UPPER_HALF_SCHEDULING) */ + MALI_DEBUG_PRINT(5, ("Mali PP scheduler: PP scheduler lock taken.\n")); +} + +MALI_STATIC_INLINE void mali_pp_scheduler_unlock(void) +{ + MALI_DEBUG_PRINT(5, ("Mali PP scheduler: Releasing PP scheduler lock.\n")); +#if defined(MALI_UPPER_HALF_SCHEDULING) + _mali_osk_spinlock_irq_unlock(pp_scheduler_lock); +#else + _mali_osk_spinlock_unlock(pp_scheduler_lock); +#endif /* defined(MALI_UPPER_HALF_SCHEDULING) */ +} + +#if defined(DEBUG) +#define MALI_ASSERT_PP_SCHEDULER_LOCKED() MALI_DEBUG_ASSERT_LOCK_HELD(pp_scheduler_lock) +#else +#define MALI_ASSERT_PP_SCHEDULER_LOCKED() do {} while (0) +#endif /* defined(DEBUG) */ + +#if defined(MALI_PP_SCHEDULER_USE_DEFERRED_JOB_DELETE) + +static _mali_osk_wq_work_t *pp_scheduler_wq_job_delete = NULL; +static _mali_osk_spinlock_irq_t *pp_scheduler_job_delete_lock = NULL; +static _MALI_OSK_LIST_HEAD_STATIC_INIT(pp_scheduler_job_deletion_queue); + +static void mali_pp_scheduler_deferred_job_delete(struct mali_pp_job *job) +{ + MALI_DEBUG_ASSERT_POINTER(job); + + _mali_osk_spinlock_irq_lock(pp_scheduler_job_delete_lock); + + /* This job object should not be on any lists. */ + MALI_DEBUG_ASSERT(_mali_osk_list_empty(&job->list)); + MALI_DEBUG_ASSERT(_mali_osk_list_empty(&job->session_list)); + MALI_DEBUG_ASSERT(_mali_osk_list_empty(&job->session_fb_lookup_list)); + + _mali_osk_list_addtail(&job->list, &pp_scheduler_job_deletion_queue); + + _mali_osk_spinlock_irq_unlock(pp_scheduler_job_delete_lock); + + _mali_osk_wq_schedule_work(pp_scheduler_wq_job_delete); +} + +static void mali_pp_scheduler_do_job_delete(void *arg) +{ + _MALI_OSK_LIST_HEAD_STATIC_INIT(list); + struct mali_pp_job *job; + struct mali_pp_job *tmp; + + MALI_IGNORE(arg); + + _mali_osk_spinlock_irq_lock(pp_scheduler_job_delete_lock); + + /* + * Quickly "unhook" the jobs pending to be deleted, so we can release the lock before + * we start deleting the job objects (without any locks held + */ + _mali_osk_list_move_list(&pp_scheduler_job_deletion_queue, &list); + + _mali_osk_spinlock_irq_unlock(pp_scheduler_job_delete_lock); + + _MALI_OSK_LIST_FOREACHENTRY(job, tmp, &list, struct mali_pp_job, list) { + mali_pp_job_delete(job); /* delete the job object itself */ + } +} + +#endif /* defined(MALI_PP_SCHEDULER_USE_DEFERRED_JOB_DELETE) */ + +#if defined(MALI_PP_SCHEDULER_USE_DEFERRED_JOB_QUEUE) + +static _mali_osk_wq_work_t *pp_scheduler_wq_job_queue = NULL; +static _mali_osk_spinlock_irq_t *pp_scheduler_job_queue_lock = NULL; +static _MALI_OSK_LIST_HEAD_STATIC_INIT(pp_scheduler_job_queue_list); + +static void mali_pp_scheduler_deferred_job_queue(struct mali_pp_job *job) +{ + MALI_DEBUG_ASSERT_POINTER(job); + + _mali_osk_spinlock_irq_lock(pp_scheduler_job_queue_lock); + _mali_osk_list_addtail(&job->list, &pp_scheduler_job_queue_list); + _mali_osk_spinlock_irq_unlock(pp_scheduler_job_queue_lock); + + _mali_osk_wq_schedule_work(pp_scheduler_wq_job_queue); +} + +static void mali_pp_scheduler_do_job_queue(void *arg) +{ + _MALI_OSK_LIST_HEAD_STATIC_INIT(list); + struct mali_pp_job *job; + struct mali_pp_job *tmp; + mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY; + + MALI_IGNORE(arg); + + _mali_osk_spinlock_irq_lock(pp_scheduler_job_queue_lock); + + /* + * Quickly "unhook" the jobs pending to be queued, so we can release the lock before + * we start queueing the job objects (without any locks held) + */ + _mali_osk_list_move_list(&pp_scheduler_job_queue_list, &list); + + _mali_osk_spinlock_irq_unlock(pp_scheduler_job_queue_lock); + + _MALI_OSK_LIST_FOREACHENTRY(job, tmp, &list, struct mali_pp_job, list) { + _mali_osk_list_delinit(&job->list); + schedule_mask |= mali_pp_scheduler_queue_job(job); + } + + mali_scheduler_schedule_from_mask(schedule_mask, MALI_FALSE); +} + +#endif /* defined(MALI_PP_SCHEDULER_USE_DEFERRED_JOB_QUEUE) */ + +MALI_STATIC_INLINE mali_bool mali_pp_scheduler_has_virtual_group(void) +{ +#if defined(CONFIG_MALI450) + return NULL != virtual_group; +#else + return MALI_FALSE; +#endif /* defined(CONFIG_MALI450) */ +} + +_mali_osk_errcode_t mali_pp_scheduler_initialize(void) +{ + _MALI_OSK_INIT_LIST_HEAD(&job_queue.normal_pri); + _MALI_OSK_INIT_LIST_HEAD(&job_queue.high_pri); + job_queue.depth = 0; + + _MALI_OSK_INIT_LIST_HEAD(&virtual_job_queue.normal_pri); + _MALI_OSK_INIT_LIST_HEAD(&virtual_job_queue.high_pri); + virtual_job_queue.depth = 0; + +#if defined(MALI_UPPER_HALF_SCHEDULING) + pp_scheduler_lock = _mali_osk_spinlock_irq_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_SCHEDULER); +#else + pp_scheduler_lock = _mali_osk_spinlock_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_SCHEDULER); +#endif /* defined(MALI_UPPER_HALF_SCHEDULING) */ + if (NULL == pp_scheduler_lock) goto cleanup; + + pp_scheduler_working_wait_queue = _mali_osk_wait_queue_init(); + if (NULL == pp_scheduler_working_wait_queue) goto cleanup; + +#if defined(MALI_PP_SCHEDULER_USE_DEFERRED_JOB_DELETE) + pp_scheduler_wq_job_delete = _mali_osk_wq_create_work(mali_pp_scheduler_do_job_delete, NULL); + if (NULL == pp_scheduler_wq_job_delete) goto cleanup; + + pp_scheduler_job_delete_lock = _mali_osk_spinlock_irq_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_SCHEDULER_DEFERRED); + if (NULL == pp_scheduler_job_delete_lock) goto cleanup; +#endif /* defined(MALI_PP_SCHEDULER_USE_DEFERRED_JOB_DELETE) */ + +#if defined(MALI_PP_SCHEDULER_USE_DEFERRED_JOB_QUEUE) + pp_scheduler_wq_job_queue = _mali_osk_wq_create_work(mali_pp_scheduler_do_job_queue, NULL); + if (NULL == pp_scheduler_wq_job_queue) goto cleanup; + + pp_scheduler_job_queue_lock = _mali_osk_spinlock_irq_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_SCHEDULER_DEFERRED); + if (NULL == pp_scheduler_job_queue_lock) goto cleanup; +#endif /* defined(MALI_PP_SCHEDULER_USE_DEFERRED_JOB_QUEUE) */ + + return _MALI_OSK_ERR_OK; + +cleanup: +#if defined(MALI_PP_SCHEDULER_USE_DEFERRED_JOB_QUEUE) + if (NULL != pp_scheduler_job_queue_lock) { + _mali_osk_spinlock_irq_term(pp_scheduler_job_queue_lock); + pp_scheduler_job_queue_lock = NULL; + } + + if (NULL != pp_scheduler_wq_job_queue) { + _mali_osk_wq_delete_work(pp_scheduler_wq_job_queue); + pp_scheduler_wq_job_queue = NULL; + } +#endif /* defined(MALI_PP_SCHEDULER_USE_DEFERRED_JOB_QUEUE) */ + +#if defined(MALI_PP_SCHEDULER_USE_DEFERRED_JOB_DELETE) + if (NULL != pp_scheduler_job_delete_lock) { + _mali_osk_spinlock_irq_term(pp_scheduler_job_delete_lock); + pp_scheduler_job_delete_lock = NULL; + } + + if (NULL != pp_scheduler_wq_job_delete) { + _mali_osk_wq_delete_work(pp_scheduler_wq_job_delete); + pp_scheduler_wq_job_delete = NULL; + } +#endif /* defined(MALI_PP_SCHEDULER_USE_DEFERRED_JOB_DELETE) */ + + if (NULL != pp_scheduler_working_wait_queue) { + _mali_osk_wait_queue_term(pp_scheduler_working_wait_queue); + pp_scheduler_working_wait_queue = NULL; + } + + if (NULL != pp_scheduler_lock) { +#if defined(MALI_UPPER_HALF_SCHEDULING) + _mali_osk_spinlock_irq_term(pp_scheduler_lock); +#else + _mali_osk_spinlock_term(pp_scheduler_lock); +#endif /* defined(MALI_UPPER_HALF_SCHEDULING) */ + pp_scheduler_lock = NULL; + } + + return _MALI_OSK_ERR_NOMEM; +} + +void mali_pp_scheduler_terminate(void) +{ +#if defined(MALI_PP_SCHEDULER_USE_DEFERRED_JOB_QUEUE) + _mali_osk_spinlock_irq_term(pp_scheduler_job_queue_lock); + _mali_osk_wq_delete_work(pp_scheduler_wq_job_queue); +#endif /* defined(MALI_PP_SCHEDULER_USE_DEFERRED_JOB_QUEUE) */ + +#if defined(MALI_PP_SCHEDULER_USE_DEFERRED_JOB_DELETE) + _mali_osk_spinlock_irq_term(pp_scheduler_job_delete_lock); + _mali_osk_wq_delete_work(pp_scheduler_wq_job_delete); +#endif /* defined(MALI_PP_SCHEDULER_USE_DEFERRED_JOB_DELETE) */ + + _mali_osk_wait_queue_term(pp_scheduler_working_wait_queue); + +#if defined(MALI_UPPER_HALF_SCHEDULING) + _mali_osk_spinlock_irq_term(pp_scheduler_lock); +#else + _mali_osk_spinlock_term(pp_scheduler_lock); +#endif /* defined(MALI_UPPER_HALF_SCHEDULING) */ +} + +void mali_pp_scheduler_populate(void) +{ + struct mali_group *group; + struct mali_pp_core *pp_core; + u32 num_groups; + u32 i; + + num_groups = mali_group_get_glob_num_groups(); + + /* Do we have a virtual group? */ + for (i = 0; i < num_groups; i++) { + group = mali_group_get_glob_group(i); + + if (mali_group_is_virtual(group)) { + MALI_DEBUG_PRINT(3, ("Mali PP scheduler: Found virtual group %p.\n", group)); + + virtual_group = group; + break; + } + } + + /* Find all the available PP cores */ + for (i = 0; i < num_groups; i++) { + group = mali_group_get_glob_group(i); + pp_core = mali_group_get_pp_core(group); + + if (NULL != pp_core && !mali_group_is_virtual(group)) { + if (0 == pp_version) { + /* Retrieve PP version from the first available PP core */ + pp_version = mali_pp_core_get_version(pp_core); + } + + if (mali_pp_scheduler_has_virtual_group()) { + /* Add all physical PP cores to the virtual group */ + mali_group_lock(virtual_group); + group->state = MALI_GROUP_STATE_JOINING_VIRTUAL; + mali_group_add_group(virtual_group, group, MALI_TRUE); + mali_group_unlock(virtual_group); + } else { + _mali_osk_list_add(&group->pp_scheduler_list, &group_list_idle); + } + + num_cores++; + } + } + + enabled_cores = num_cores; +} + +void mali_pp_scheduler_depopulate(void) +{ + struct mali_group *group, *temp; + + MALI_DEBUG_ASSERT(_mali_osk_list_empty(&group_list_working)); + MALI_DEBUG_ASSERT(VIRTUAL_GROUP_WORKING != virtual_group_state); + + /* Delete all groups owned by scheduler */ + if (mali_pp_scheduler_has_virtual_group()) { + mali_group_delete(virtual_group); + } + + _MALI_OSK_LIST_FOREACHENTRY(group, temp, &group_list_idle, struct mali_group, pp_scheduler_list) { + mali_group_delete(group); + } + _MALI_OSK_LIST_FOREACHENTRY(group, temp, &group_list_disabled, struct mali_group, pp_scheduler_list) { + mali_group_delete(group); + } +} + +MALI_STATIC_INLINE void mali_pp_scheduler_disable_empty_virtual(void) +{ + MALI_ASSERT_GROUP_LOCKED(virtual_group); + + if (mali_group_virtual_disable_if_empty(virtual_group)) { + MALI_DEBUG_PRINT(4, ("Disabling empty virtual group\n")); + + MALI_DEBUG_ASSERT(VIRTUAL_GROUP_IDLE == virtual_group_state); + + virtual_group_state = VIRTUAL_GROUP_DISABLED; + } +} + +MALI_STATIC_INLINE void mali_pp_scheduler_enable_empty_virtual(void) +{ + MALI_ASSERT_GROUP_LOCKED(virtual_group); + + if (mali_group_virtual_enable_if_empty(virtual_group)) { + MALI_DEBUG_PRINT(4, ("Re-enabling empty virtual group\n")); + + MALI_DEBUG_ASSERT(VIRTUAL_GROUP_DISABLED == virtual_group_state); + + virtual_group_state = VIRTUAL_GROUP_IDLE; + } +} + +static struct mali_pp_job *mali_pp_scheduler_get_job(struct mali_pp_scheduler_job_queue *queue) +{ + struct mali_pp_job *job = NULL; + + MALI_ASSERT_PP_SCHEDULER_LOCKED(); + MALI_DEBUG_ASSERT_POINTER(queue); + + /* Check if we have a normal priority job. */ + if (!_mali_osk_list_empty(&queue->normal_pri)) { + MALI_DEBUG_ASSERT(queue->depth > 0); + job = _MALI_OSK_LIST_ENTRY(queue->normal_pri.next, struct mali_pp_job, list); + } + + /* Prefer normal priority job if it is in progress. */ + if (NULL != job && 0 < job->sub_jobs_started) { + return job; + } + + /* Check if we have a high priority job. */ + if (!_mali_osk_list_empty(&queue->high_pri)) { + MALI_DEBUG_ASSERT(queue->depth > 0); + job = _MALI_OSK_LIST_ENTRY(queue->high_pri.next, struct mali_pp_job, list); + } + + return job; +} + +/** + * Returns a physical job if a physical job is ready to run + */ +MALI_STATIC_INLINE struct mali_pp_job *mali_pp_scheduler_get_physical_job(void) +{ + MALI_ASSERT_PP_SCHEDULER_LOCKED(); + return mali_pp_scheduler_get_job(&job_queue); +} + +MALI_STATIC_INLINE void mali_pp_scheduler_dequeue_physical_job(struct mali_pp_job *job) +{ + MALI_ASSERT_PP_SCHEDULER_LOCKED(); + MALI_DEBUG_ASSERT(job_queue.depth > 0); + + /* Remove job from queue */ + if (!mali_pp_job_has_unstarted_sub_jobs(job)) { + /* All sub jobs have been started: remove job from queue */ + _mali_osk_list_delinit(&job->list); + _mali_osk_list_delinit(&job->session_fb_lookup_list); + } + + --job_queue.depth; +} + +/** + * Returns a virtual job if a virtual job is ready to run + */ +MALI_STATIC_INLINE struct mali_pp_job *mali_pp_scheduler_get_virtual_job(void) +{ + MALI_ASSERT_PP_SCHEDULER_LOCKED(); + MALI_DEBUG_ASSERT_POINTER(virtual_group); + return mali_pp_scheduler_get_job(&virtual_job_queue); +} + +MALI_STATIC_INLINE void mali_pp_scheduler_dequeue_virtual_job(struct mali_pp_job *job) +{ + MALI_ASSERT_PP_SCHEDULER_LOCKED(); + MALI_DEBUG_ASSERT(virtual_job_queue.depth > 0); + + /* Remove job from queue */ + _mali_osk_list_delinit(&job->list); + _mali_osk_list_delinit(&job->session_fb_lookup_list); + --virtual_job_queue.depth; +} + +/** + * Checks if the criteria is met for removing a physical core from virtual group + */ +MALI_STATIC_INLINE mali_bool mali_pp_scheduler_can_move_virtual_to_physical(void) +{ + MALI_ASSERT_PP_SCHEDULER_LOCKED(); + MALI_DEBUG_ASSERT(mali_pp_scheduler_has_virtual_group()); + MALI_ASSERT_GROUP_LOCKED(virtual_group); + /* + * The criteria for taking out a physical group from a virtual group are the following: + * - There virtual group is idle + * - There are currently no physical groups (idle and working) + * - There are physical jobs to be scheduled + */ + return (VIRTUAL_GROUP_IDLE == virtual_group_state) && + _mali_osk_list_empty(&group_list_idle) && + _mali_osk_list_empty(&group_list_working) && + (NULL != mali_pp_scheduler_get_physical_job()); +} + +MALI_STATIC_INLINE struct mali_group *mali_pp_scheduler_acquire_physical_group(void) +{ + MALI_ASSERT_PP_SCHEDULER_LOCKED(); + + if (!_mali_osk_list_empty(&group_list_idle)) { + MALI_DEBUG_PRINT(4, ("Mali PP scheduler: Acquiring physical group from idle list.\n")); + return _MALI_OSK_LIST_ENTRY(group_list_idle.next, struct mali_group, pp_scheduler_list); + } else if (mali_pp_scheduler_has_virtual_group()) { + MALI_ASSERT_GROUP_LOCKED(virtual_group); + if (mali_pp_scheduler_can_move_virtual_to_physical()) { + struct mali_group *group; + MALI_DEBUG_PRINT(4, ("Mali PP scheduler: Acquiring physical group from virtual group.\n")); + group = mali_group_acquire_group(virtual_group); + + if (mali_pp_scheduler_has_virtual_group()) { + mali_pp_scheduler_disable_empty_virtual(); + } + + return group; + } + } + + return NULL; +} + +static void mali_pp_scheduler_return_job_to_user(struct mali_pp_job *job, mali_bool deferred) +{ + if (MALI_FALSE == mali_pp_job_use_no_notification(job)) { + u32 i; + u32 num_counters_to_copy; + mali_bool success = mali_pp_job_was_success(job); + + _mali_uk_pp_job_finished_s *jobres = job->finished_notification->result_buffer; + _mali_osk_memset(jobres, 0, sizeof(_mali_uk_pp_job_finished_s)); /* @@@@ can be removed once we initialize all members in this struct */ + jobres->user_job_ptr = mali_pp_job_get_user_id(job); + if (MALI_TRUE == success) { + jobres->status = _MALI_UK_JOB_STATUS_END_SUCCESS; + } else { + jobres->status = _MALI_UK_JOB_STATUS_END_UNKNOWN_ERR; + } + + if (mali_pp_job_is_virtual(job)) { + num_counters_to_copy = num_cores; /* Number of physical cores available */ + } else { + num_counters_to_copy = mali_pp_job_get_sub_job_count(job); + } + + for (i = 0; i < num_counters_to_copy; i++) { + jobres->perf_counter0[i] = mali_pp_job_get_perf_counter_value0(job, i); + jobres->perf_counter1[i] = mali_pp_job_get_perf_counter_value1(job, i); + jobres->perf_counter_src0 = mali_pp_job_get_pp_counter_global_src0(); + jobres->perf_counter_src1 = mali_pp_job_get_pp_counter_global_src1(); + } + + mali_session_send_notification(mali_pp_job_get_session(job), job->finished_notification); + job->finished_notification = NULL; + } + +#if defined(MALI_PP_SCHEDULER_USE_DEFERRED_JOB_DELETE) + if (MALI_TRUE == deferred) { + /* The deletion of the job object (releasing sync refs etc) must be done in a different context */ + mali_pp_scheduler_deferred_job_delete(job); + } else { + mali_pp_job_delete(job); + } +#else + MALI_DEBUG_ASSERT(MALI_FALSE == deferred); /* no use cases need this in this configuration */ + mali_pp_job_delete(job); +#endif +} + +static void mali_pp_scheduler_finalize_job(struct mali_pp_job * job) +{ + /* This job object should not be on any lists. */ + MALI_DEBUG_ASSERT(_mali_osk_list_empty(&job->list)); + MALI_DEBUG_ASSERT(_mali_osk_list_empty(&job->session_list)); + MALI_DEBUG_ASSERT(_mali_osk_list_empty(&job->session_fb_lookup_list)); + + /* Send notification back to user space */ +#if defined(MALI_PP_SCHEDULER_USE_DEFERRED_JOB_DELETE) + mali_pp_scheduler_return_job_to_user(job, MALI_TRUE); +#else + mali_pp_scheduler_return_job_to_user(job, MALI_FALSE); +#endif + +#if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY) + if (_MALI_PP_JOB_FLAG_IS_WINDOW_SURFACE & job->uargs.flags) { + _mali_osk_atomic_inc(&job->session->number_of_window_jobs); + } +#endif + + mali_pp_scheduler_job_completed(); +} + +void mali_pp_scheduler_schedule(void) +{ + struct mali_group* physical_groups_to_start[MALI_MAX_NUMBER_OF_PP_GROUPS - 1]; + struct mali_pp_job* physical_jobs_to_start[MALI_MAX_NUMBER_OF_PP_GROUPS - 1]; + u32 physical_sub_jobs_to_start[MALI_MAX_NUMBER_OF_PP_GROUPS - 1]; + int num_physical_jobs_to_start = 0; + int i; + + if (mali_pp_scheduler_has_virtual_group()) { + /* Lock the virtual group since we might have to grab physical groups. */ + mali_group_lock(virtual_group); + } + + mali_pp_scheduler_lock(); + if (pause_count > 0) { + /* Scheduler is suspended, don't schedule any jobs. */ + mali_pp_scheduler_unlock(); + if (mali_pp_scheduler_has_virtual_group()) { + mali_group_unlock(virtual_group); + } + return; + } + + /* Find physical job(s) to schedule first. */ + while (1) { + struct mali_group *group; + struct mali_pp_job *job; + u32 sub_job; + + job = mali_pp_scheduler_get_physical_job(); + if (NULL == job) { + break; /* No job, early out. */ + } + + if (mali_scheduler_hint_is_enabled(MALI_SCHEDULER_HINT_GP_BOUND) && + mali_pp_job_is_large_and_unstarted(job) && !_mali_osk_list_empty(&group_list_working)) { + /* Since not all groups are idle, don't schedule yet. */ + break; + } + + MALI_DEBUG_ASSERT(!mali_pp_job_is_virtual(job)); + MALI_DEBUG_ASSERT(mali_pp_job_has_unstarted_sub_jobs(job)); + MALI_DEBUG_ASSERT(1 <= mali_pp_job_get_sub_job_count(job)); + + /* Acquire a physical group, either from the idle list or from the virtual group. + * In case the group was acquired from the virtual group, it's state will be + * LEAVING_VIRTUAL and must be set to IDLE before it can be used. */ + group = mali_pp_scheduler_acquire_physical_group(); + if (NULL == group) { + /* Could not get a group to run the job on, early out. */ + MALI_DEBUG_PRINT(4, ("Mali PP scheduler: No more physical groups available.\n")); + break; + } + + MALI_DEBUG_PRINT(4, ("Mali PP scheduler: Acquired physical group %p.\n", group)); + + /* Mark sub job as started. */ + sub_job = mali_pp_job_get_first_unstarted_sub_job(job); + mali_pp_job_mark_sub_job_started(job, sub_job); + + /* Remove job from queue (if this was the last sub job). */ + mali_pp_scheduler_dequeue_physical_job(job); + + /* Move group to working list. */ + _mali_osk_list_move(&(group->pp_scheduler_list), &group_list_working); + + /* Keep track of this group, so that we actually can start the job once we are done with the scheduler lock we are now holding. */ + physical_groups_to_start[num_physical_jobs_to_start] = group; + physical_jobs_to_start[num_physical_jobs_to_start] = job; + physical_sub_jobs_to_start[num_physical_jobs_to_start] = sub_job; + ++num_physical_jobs_to_start; + + MALI_DEBUG_ASSERT(num_physical_jobs_to_start < MALI_MAX_NUMBER_OF_PP_GROUPS); + } + + if (mali_pp_scheduler_has_virtual_group()) { + if (VIRTUAL_GROUP_IDLE == virtual_group_state) { + /* We have a virtual group and it is idle. */ + + struct mali_pp_job *job; + + /* Find a virtual job we can start. */ + job = mali_pp_scheduler_get_virtual_job(); + + if (NULL != job) { + MALI_DEBUG_ASSERT(mali_pp_job_is_virtual(job)); + MALI_DEBUG_ASSERT(mali_pp_job_has_unstarted_sub_jobs(job)); + MALI_DEBUG_ASSERT(1 == mali_pp_job_get_sub_job_count(job)); + + /* Mark the one and only sub job as started. */ + mali_pp_job_mark_sub_job_started(job, 0); + + /* Remove job from queue. */ + mali_pp_scheduler_dequeue_virtual_job(job); + + /* Virtual group is now working. */ + virtual_group_state = VIRTUAL_GROUP_WORKING; + + /* We no longer need the scheduler lock, but we still need the virtual lock + * in order to start the virtual job. */ + mali_pp_scheduler_unlock(); + + /* Start job. */ + mali_group_start_pp_job(virtual_group, job, 0); + + MALI_DEBUG_PRINT(4, ("Mali PP scheduler: Virtual job %u (0x%08X) part %u/%u started (from schedule).\n", + mali_pp_job_get_id(job), job, 1, + mali_pp_job_get_sub_job_count(job))); + + mali_group_unlock(virtual_group); + } else { + /* No virtual job to start. */ + mali_pp_scheduler_unlock(); + mali_group_unlock(virtual_group); + } + } else { + /* We have a virtual group, but it is busy or disabled. */ + MALI_DEBUG_ASSERT(VIRTUAL_GROUP_IDLE != virtual_group_state); + + mali_pp_scheduler_unlock(); + mali_group_unlock(virtual_group); + } + } else { + /* There is no virtual group. */ + mali_pp_scheduler_unlock(); + } + + /* We have now released the scheduler lock, and we are ready to start the physical jobs. + * The reason we want to wait until we have released the scheduler lock is that job start + * may take quite a bit of time (many registers have to be written). This will allow new + * jobs from user space to come in, and post-processing of other PP jobs to happen at the + * same time as we start jobs. */ + for (i = 0; i < num_physical_jobs_to_start; i++) { + struct mali_group *group = physical_groups_to_start[i]; + struct mali_pp_job *job = physical_jobs_to_start[i]; + u32 sub_job = physical_sub_jobs_to_start[i]; + + MALI_DEBUG_ASSERT_POINTER(group); + MALI_DEBUG_ASSERT_POINTER(job); + MALI_DEBUG_ASSERT(!mali_group_is_virtual(group)); + MALI_DEBUG_ASSERT(!mali_pp_job_is_virtual(job)); + + mali_group_lock(group); + + /* Set state to IDLE if group was acquired from the virtual group. */ + group->state = MALI_GROUP_STATE_IDLE; + + mali_group_start_pp_job(group, job, sub_job); + + MALI_DEBUG_PRINT(4, ("Mali PP scheduler: Physical job %u (0x%08X) part %u/%u started (from schedule).\n", + mali_pp_job_get_id(job), job, sub_job + 1, + mali_pp_job_get_sub_job_count(job))); + + mali_group_unlock(group); + } +} + +/** + * Set group idle. + * + * If @ref group is the virtual group, nothing is done since the virtual group should be idle + * already. + * + * If @ref group is a physical group we rejoin the virtual group, if it exists. If not, we move the + * physical group to the idle list. + * + * @note The group and the scheduler must both be locked when entering this function. Both will be + * unlocked before exiting. + * + * @param group The group to set idle. + */ +static void mali_pp_scheduler_set_group_idle_and_unlock(struct mali_group *group) +{ + MALI_DEBUG_ASSERT_POINTER(group); + + MALI_ASSERT_GROUP_LOCKED(group); + MALI_DEBUG_ASSERT_LOCK_HELD(pp_scheduler_lock); + + if (mali_group_is_virtual(group)) { + /* The virtual group should have been set to non-working already. */ + MALI_DEBUG_ASSERT(VIRTUAL_GROUP_IDLE == virtual_group_state); + + mali_pp_scheduler_unlock(); + mali_group_unlock(group); + + return; + } else { + if (mali_pp_scheduler_has_virtual_group()) { + /* Rejoin virtual group. */ + + /* We're no longer needed on the scheduler list. */ + _mali_osk_list_delinit(&(group->pp_scheduler_list)); + + /* Make sure no interrupts are handled for this group during the transition + * from physical to virtual. */ + group->state = MALI_GROUP_STATE_JOINING_VIRTUAL; + + mali_pp_scheduler_unlock(); + mali_group_unlock(group); + + mali_group_lock(virtual_group); + + if (mali_pp_scheduler_has_virtual_group()) { + mali_pp_scheduler_enable_empty_virtual(); + } + + /* We need to recheck the group state since it is possible that someone has + * modified the group before we locked the virtual group. */ + if (MALI_GROUP_STATE_JOINING_VIRTUAL == group->state) { + mali_group_add_group(virtual_group, group, MALI_TRUE); + } + + mali_group_unlock(virtual_group); + } else { + /* Move physical group back to idle list. */ + _mali_osk_list_move(&(group->pp_scheduler_list), &group_list_idle); + +#if defined(CONFIG_GPU_TRACEPOINTS) && defined(CONFIG_TRACEPOINTS) + trace_gpu_sched_switch(mali_pp_get_hw_core_desc(group->pp_core), sched_clock(), 0, 0, 0); +#endif + + mali_pp_scheduler_unlock(); + mali_group_unlock(group); + } + } +} + +/** + * Schedule job on locked group. + * + * @note The group and the scheduler must both be locked when entering this function. Both will be + * unlocked before exiting. + * + * @param group The group to schedule on. + */ +static void mali_pp_scheduler_schedule_on_group_and_unlock(struct mali_group *group) +{ + MALI_DEBUG_ASSERT_POINTER(group); + + MALI_ASSERT_GROUP_LOCKED(group); + MALI_DEBUG_ASSERT_LOCK_HELD(pp_scheduler_lock); + + if (mali_group_is_virtual(group)) { + /* Now that the virtual group is idle, check if we should reconfigure. */ + + struct mali_pp_job *virtual_job = NULL; + struct mali_pp_job *physical_job = NULL; + struct mali_group *physical_group = NULL; + u32 physical_sub_job = 0; + + MALI_DEBUG_ASSERT(VIRTUAL_GROUP_IDLE == virtual_group_state); + + if (mali_pp_scheduler_can_move_virtual_to_physical()) { + /* There is a runnable physical job and we can acquire a physical group. */ + physical_job = mali_pp_scheduler_get_physical_job(); + MALI_DEBUG_ASSERT_POINTER(physical_job); + MALI_DEBUG_ASSERT(mali_pp_job_has_unstarted_sub_jobs(physical_job)); + + /* Mark sub job as started. */ + physical_sub_job = mali_pp_job_get_first_unstarted_sub_job(physical_job); + mali_pp_job_mark_sub_job_started(physical_job, physical_sub_job); + + /* Remove job from queue (if this was the last sub job). */ + mali_pp_scheduler_dequeue_physical_job(physical_job); + + /* Acquire a physical group from the virtual group. Its state will + * be LEAVING_VIRTUAL and must be set to IDLE before it can be + * used. */ + physical_group = mali_group_acquire_group(virtual_group); + + /* Move physical group to the working list, as we will soon start a job on it. */ + _mali_osk_list_move(&(physical_group->pp_scheduler_list), &group_list_working); + + mali_pp_scheduler_disable_empty_virtual(); + } + + /* Get next virtual job. */ + virtual_job = mali_pp_scheduler_get_virtual_job(); + if (NULL != virtual_job && VIRTUAL_GROUP_IDLE == virtual_group_state) { + /* There is a runnable virtual job. */ + + MALI_DEBUG_ASSERT(mali_pp_job_is_virtual(virtual_job)); + MALI_DEBUG_ASSERT(mali_pp_job_has_unstarted_sub_jobs(virtual_job)); + MALI_DEBUG_ASSERT(1 == mali_pp_job_get_sub_job_count(virtual_job)); + + mali_pp_job_mark_sub_job_started(virtual_job, 0); + + /* Remove job from queue. */ + mali_pp_scheduler_dequeue_virtual_job(virtual_job); + + /* Virtual group is now working. */ + virtual_group_state = VIRTUAL_GROUP_WORKING; + + mali_pp_scheduler_unlock(); + + /* Start job. */ + mali_group_start_pp_job(group, virtual_job, 0); + + MALI_DEBUG_PRINT(4, ("Mali PP scheduler: Virtual job %u (0x%08X) part %u/%u started (from job_done).\n", + mali_pp_job_get_id(virtual_job), virtual_job, 1, + mali_pp_job_get_sub_job_count(virtual_job))); + } else { +#if defined(CONFIG_GPU_TRACEPOINTS) && defined(CONFIG_TRACEPOINTS) + trace_gpu_sched_switch("Mali_Virtual_PP", sched_clock(), 0, 0, 0); +#endif + + mali_pp_scheduler_unlock(); + } + + /* Releasing the virtual group lock that was held when entering the function. */ + mali_group_unlock(group); + + /* Start a physical job (if we acquired a physical group earlier). */ + if (NULL != physical_job && NULL != physical_group) { + mali_group_lock(physical_group); + + /* Change the group state from LEAVING_VIRTUAL to IDLE to complete the transition. */ + physical_group->state = MALI_GROUP_STATE_IDLE; + + /* Start job. */ + mali_group_start_pp_job(physical_group, physical_job, physical_sub_job); + + MALI_DEBUG_PRINT(4, ("Mali PP scheduler: Physical job %u (0x%08X) part %u/%u started (from job_done).\n", + mali_pp_job_get_id(physical_job), physical_job, physical_sub_job + 1, + mali_pp_job_get_sub_job_count(physical_job))); + + mali_group_unlock(physical_group); + } + } else { + /* Physical group. */ + struct mali_pp_job *job = NULL; + u32 sub_job = 0; + + job = mali_pp_scheduler_get_physical_job(); + if (NULL != job) { + /* There is a runnable physical job. */ + MALI_DEBUG_ASSERT(mali_pp_job_has_unstarted_sub_jobs(job)); + + /* Mark sub job as started. */ + sub_job = mali_pp_job_get_first_unstarted_sub_job(job); + mali_pp_job_mark_sub_job_started(job, sub_job); + + /* Remove job from queue (if this was the last sub job). */ + mali_pp_scheduler_dequeue_physical_job(job); + + mali_pp_scheduler_unlock(); + + /* Group is already on the working list, so start the new job. */ + mali_group_start_pp_job(group, job, sub_job); + + MALI_DEBUG_PRINT(4, ("Mali PP scheduler: Physical job %u (0x%08X) part %u/%u started (from job_done).\n", + mali_pp_job_get_id(job), job, sub_job + 1, mali_pp_job_get_sub_job_count(job))); + + mali_group_unlock(group); + } else { + mali_pp_scheduler_set_group_idle_and_unlock(group); + } + } +} + +void mali_pp_scheduler_job_done(struct mali_group *group, struct mali_pp_job *job, u32 sub_job, mali_bool success, mali_bool in_upper_half) +{ + mali_bool job_is_done = MALI_FALSE; + mali_bool schedule_on_group = MALI_FALSE; + mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY; + + MALI_DEBUG_PRINT(3, ("Mali PP scheduler: %s job %u (0x%08X) part %u/%u completed (%s).\n", + mali_pp_job_is_virtual(job) ? "Virtual" : "Physical", + mali_pp_job_get_id(job), + job, sub_job + 1, + mali_pp_job_get_sub_job_count(job), + success ? "success" : "failure")); + + MALI_ASSERT_GROUP_LOCKED(group); + mali_pp_scheduler_lock(); + + mali_pp_job_mark_sub_job_completed(job, success); + + MALI_DEBUG_ASSERT(mali_pp_job_is_virtual(job) == mali_group_is_virtual(group)); + + job_is_done = mali_pp_job_is_complete(job); + + if (job_is_done) { + /* Job is removed from these lists when the last sub job is scheduled. */ + MALI_DEBUG_ASSERT(_mali_osk_list_empty(&job->list)); + MALI_DEBUG_ASSERT(_mali_osk_list_empty(&job->session_fb_lookup_list)); + + /* Remove job from session list. */ + _mali_osk_list_delinit(&job->session_list); + + MALI_DEBUG_PRINT(4, ("Mali PP scheduler: All parts completed for %s job %u (0x%08X).\n", + mali_pp_job_is_virtual(job) ? "virtual" : "physical", + mali_pp_job_get_id(job), job)); + + mali_pp_scheduler_unlock(); + + /* Release tracker. If other trackers are waiting on this tracker, this could + * trigger activation. The returned scheduling mask can be used to determine if we + * have to schedule GP, PP or both. */ + schedule_mask = mali_timeline_tracker_release(&job->tracker); + + mali_pp_scheduler_lock(); + } + + if (mali_group_is_virtual(group)) { + /* Obey the policy. */ + virtual_group_state = VIRTUAL_GROUP_IDLE; + } + + /* If paused, then this was the last job, so wake up sleeping workers and return. */ + if (pause_count > 0) { + /* Wake up sleeping workers. Their wake-up condition is that + * num_slots == num_slots_idle, so unless we are done working, no + * threads will actually be woken up. + */ + if (!mali_group_is_virtual(group)) { + /* Move physical group to idle list. */ + _mali_osk_list_move(&(group->pp_scheduler_list), &group_list_idle); + } + +#if defined(CONFIG_GPU_TRACEPOINTS) && defined(CONFIG_TRACEPOINTS) + trace_gpu_sched_switch(mali_pp_get_hw_core_desc(group->pp_core), sched_clock(), 0, 0, 0); +#endif + + _mali_osk_wait_queue_wake_up(pp_scheduler_working_wait_queue); + + mali_pp_scheduler_unlock(); + mali_group_unlock(group); + + if (job_is_done) { + /* Return job to user and delete it. */ + mali_pp_scheduler_finalize_job(job); + } + + /* A GP job might be queued by tracker release above, + * make sure GP scheduler gets a chance to schedule this (if possible) + */ + mali_scheduler_schedule_from_mask(schedule_mask & ~MALI_SCHEDULER_MASK_PP, in_upper_half); + + return; + } + + /* Since this group just finished running a job, we can reschedule a new job on it + * immediately. */ + + /* By default, don't schedule on group. */ + schedule_on_group = MALI_FALSE; + + if (mali_group_is_virtual(group)) { + /* Always schedule immediately on virtual group. */ + schedule_mask &= ~MALI_SCHEDULER_MASK_PP; + schedule_on_group = MALI_TRUE; + } else if (0 < job_queue.depth && (!mali_scheduler_mask_is_set(schedule_mask, MALI_SCHEDULER_MASK_PP) || _mali_osk_list_empty(&group_list_idle))) { + struct mali_pp_job *next_job = NULL; + + next_job = mali_pp_scheduler_get_physical_job(); + MALI_DEBUG_ASSERT_POINTER(next_job); + + /* If no new jobs have been queued or if this group is the only idle group, we can + * schedule immediately on this group, unless we are GP bound and the next job would + * benefit from all its sub jobs being started concurrently. */ + + if (mali_scheduler_hint_is_enabled(MALI_SCHEDULER_HINT_GP_BOUND) && mali_pp_job_is_large_and_unstarted(next_job)) { + /* We are GP bound and the job would benefit from all sub jobs being started + * concurrently. Postpone scheduling until after group has been unlocked. */ + schedule_mask |= MALI_SCHEDULER_MASK_PP; + schedule_on_group = MALI_FALSE; + } else { + /* Schedule job immediately since we are not GP bound. */ + schedule_mask &= ~MALI_SCHEDULER_MASK_PP; + schedule_on_group = MALI_TRUE; + } + } + + if (schedule_on_group) { + /* Schedule a new job on this group. */ + mali_pp_scheduler_schedule_on_group_and_unlock(group); + } else { + /* Set group idle. Will rejoin virtual group, under appropriate conditions. */ + mali_pp_scheduler_set_group_idle_and_unlock(group); + } + + if (!schedule_on_group || MALI_SCHEDULER_MASK_EMPTY != schedule_mask) { + if (MALI_SCHEDULER_MASK_PP & schedule_mask) { + /* Schedule PP directly. */ + mali_pp_scheduler_schedule(); + schedule_mask &= ~MALI_SCHEDULER_MASK_PP; + } + + /* Schedule other jobs that were activated. */ + mali_scheduler_schedule_from_mask(schedule_mask, in_upper_half); + } + + if (job_is_done) { + /* Return job to user and delete it. */ + mali_pp_scheduler_finalize_job(job); + } +} + +void mali_pp_scheduler_suspend(void) +{ + mali_pp_scheduler_lock(); + pause_count++; /* Increment the pause_count so that no more jobs will be scheduled */ + mali_pp_scheduler_unlock(); + + /* Go to sleep. When woken up again (in mali_pp_scheduler_job_done), the + * mali_pp_scheduler_suspended() function will be called. This will return true + * if state is idle and pause_count > 0, so if the core is active this + * will not do anything. + */ + _mali_osk_wait_queue_wait_event(pp_scheduler_working_wait_queue, mali_pp_scheduler_is_suspended, NULL); +} + +void mali_pp_scheduler_resume(void) +{ + mali_pp_scheduler_lock(); + pause_count--; /* Decrement pause_count to allow scheduling again (if it reaches 0) */ + mali_pp_scheduler_unlock(); + if (0 == pause_count) { + mali_pp_scheduler_schedule(); + } +} + +mali_timeline_point mali_pp_scheduler_submit_job(struct mali_session_data *session, struct mali_pp_job *job) +{ + mali_timeline_point point; + u32 fb_lookup_id = 0; + + MALI_DEBUG_ASSERT_POINTER(session); + MALI_DEBUG_ASSERT_POINTER(job); + + mali_pp_scheduler_lock(); + + fb_lookup_id = mali_pp_job_get_fb_lookup_id(job); + MALI_DEBUG_ASSERT(MALI_PP_JOB_FB_LOOKUP_LIST_SIZE > fb_lookup_id); + + /* Adding job to the lookup list used to quickly discard writeback units of queued jobs. */ + _mali_osk_list_addtail(&job->session_fb_lookup_list, &session->pp_job_fb_lookup_list[fb_lookup_id]); + + mali_pp_scheduler_unlock(); + + mali_pp_scheduler_job_queued(); + + /* Add job to Timeline system. */ + point = mali_timeline_system_add_tracker(session->timeline_system, &job->tracker, MALI_TIMELINE_PP); + + return point; +} + +_mali_osk_errcode_t _mali_ukk_pp_start_job(void *ctx, _mali_uk_pp_start_job_s *uargs) +{ + struct mali_session_data *session; + struct mali_pp_job *job; + mali_timeline_point point; + u32 __user *timeline_point_ptr = NULL; + + MALI_DEBUG_ASSERT_POINTER(uargs); + MALI_DEBUG_ASSERT_POINTER(ctx); + + session = (struct mali_session_data*)ctx; + + job = mali_pp_job_create(session, uargs, mali_scheduler_get_new_id()); + if (NULL == job) { + MALI_PRINT_ERROR(("Failed to create PP job.\n")); + return _MALI_OSK_ERR_NOMEM; + } + + timeline_point_ptr = (u32 __user *) job->uargs.timeline_point_ptr; + + point = mali_pp_scheduler_submit_job(session, job); + job = NULL; + + if (0 != _mali_osk_put_user(((u32) point), timeline_point_ptr)) { + /* Let user space know that something failed after the job was started. */ + return _MALI_OSK_ERR_ITEM_NOT_FOUND; + } + + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t _mali_ukk_pp_and_gp_start_job(void *ctx, _mali_uk_pp_and_gp_start_job_s *uargs) +{ + struct mali_session_data *session; + _mali_uk_pp_and_gp_start_job_s kargs; + struct mali_pp_job *pp_job; + struct mali_gp_job *gp_job; + u32 __user *timeline_point_ptr = NULL; + mali_timeline_point point; + + MALI_DEBUG_ASSERT_POINTER(ctx); + MALI_DEBUG_ASSERT_POINTER(uargs); + + session = (struct mali_session_data *) ctx; + + if (0 != _mali_osk_copy_from_user(&kargs, uargs, sizeof(_mali_uk_pp_and_gp_start_job_s))) { + return _MALI_OSK_ERR_NOMEM; + } + + pp_job = mali_pp_job_create(session, kargs.pp_args, mali_scheduler_get_new_id()); + if (NULL == pp_job) { + MALI_PRINT_ERROR(("Failed to create PP job.\n")); + return _MALI_OSK_ERR_NOMEM; + } + + gp_job = mali_gp_job_create(session, kargs.gp_args, mali_scheduler_get_new_id(), mali_pp_job_get_tracker(pp_job)); + if (NULL == gp_job) { + MALI_PRINT_ERROR(("Failed to create GP job.\n")); + mali_pp_job_delete(pp_job); + return _MALI_OSK_ERR_NOMEM; + } + + timeline_point_ptr = (u32 __user *) pp_job->uargs.timeline_point_ptr; + + /* Submit GP job. */ + mali_gp_scheduler_submit_job(session, gp_job); + gp_job = NULL; + + /* Submit PP job. */ + point = mali_pp_scheduler_submit_job(session, pp_job); + pp_job = NULL; + + if (0 != _mali_osk_put_user(((u32) point), timeline_point_ptr)) { + /* Let user space know that something failed after the jobs were started. */ + return _MALI_OSK_ERR_ITEM_NOT_FOUND; + } + + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t _mali_ukk_get_pp_number_of_cores(_mali_uk_get_pp_number_of_cores_s *args) +{ + MALI_DEBUG_ASSERT_POINTER(args); + MALI_DEBUG_ASSERT_POINTER(args->ctx); + args->number_of_total_cores = num_cores; + args->number_of_enabled_cores = enabled_cores; + return _MALI_OSK_ERR_OK; +} + +u32 mali_pp_scheduler_get_num_cores_total(void) +{ + return num_cores; +} + +u32 mali_pp_scheduler_get_num_cores_enabled(void) +{ + return enabled_cores; +} + +_mali_osk_errcode_t _mali_ukk_get_pp_core_version(_mali_uk_get_pp_core_version_s *args) +{ + MALI_DEBUG_ASSERT_POINTER(args); + MALI_DEBUG_ASSERT_POINTER(args->ctx); + args->version = pp_version; + return _MALI_OSK_ERR_OK; +} + +void _mali_ukk_pp_job_disable_wb(_mali_uk_pp_disable_wb_s *args) +{ + struct mali_session_data *session; + struct mali_pp_job *job; + struct mali_pp_job *tmp; + u32 fb_lookup_id; + + MALI_DEBUG_ASSERT_POINTER(args); + MALI_DEBUG_ASSERT_POINTER(args->ctx); + + session = (struct mali_session_data*)args->ctx; + + fb_lookup_id = args->fb_id & MALI_PP_JOB_FB_LOOKUP_LIST_MASK; + + mali_pp_scheduler_lock(); + + /* Iterate over all jobs for given frame builder_id. */ + _MALI_OSK_LIST_FOREACHENTRY(job, tmp, &session->pp_job_fb_lookup_list[fb_lookup_id], struct mali_pp_job, session_fb_lookup_list) { + MALI_DEBUG_CODE(u32 disable_mask = 0); + + if (mali_pp_job_get_frame_builder_id(job) == (u32) args->fb_id) { + MALI_DEBUG_CODE(disable_mask |= 0xD<<(4*3)); + if (args->wb0_memory == job->uargs.wb0_registers[MALI200_REG_ADDR_WB_SOURCE_ADDR/sizeof(u32)]) { + MALI_DEBUG_CODE(disable_mask |= 0x1<<(4*1)); + mali_pp_job_disable_wb0(job); + } + if (args->wb1_memory == job->uargs.wb1_registers[MALI200_REG_ADDR_WB_SOURCE_ADDR/sizeof(u32)]) { + MALI_DEBUG_CODE(disable_mask |= 0x2<<(4*2)); + mali_pp_job_disable_wb1(job); + } + if (args->wb2_memory == job->uargs.wb2_registers[MALI200_REG_ADDR_WB_SOURCE_ADDR/sizeof(u32)]) { + MALI_DEBUG_CODE(disable_mask |= 0x3<<(4*3)); + mali_pp_job_disable_wb2(job); + } + MALI_DEBUG_PRINT(3, ("Mali PP scheduler: Disable WB: 0x%X.\n", disable_mask)); + } else { + MALI_DEBUG_PRINT(4, ("Mali PP scheduler: Disable WB mismatching FB.\n")); + } + } + + mali_pp_scheduler_unlock(); +} + +void mali_pp_scheduler_abort_session(struct mali_session_data *session) +{ + u32 i = 0; + struct mali_pp_job *job, *tmp_job; + struct mali_group *group, *tmp_group; + struct mali_group *groups[MALI_MAX_NUMBER_OF_GROUPS]; + _MALI_OSK_LIST_HEAD_STATIC_INIT(removed_jobs); + + MALI_DEBUG_ASSERT_POINTER(session); + MALI_DEBUG_ASSERT(session->is_aborting); + + MALI_DEBUG_PRINT(3, ("Mali PP scheduler: Aborting all jobs from session 0x%08X.\n", session)); + + mali_pp_scheduler_lock(); + + /* Find all jobs from the aborting session. */ + _MALI_OSK_LIST_FOREACHENTRY(job, tmp_job, &session->pp_job_list, struct mali_pp_job, session_list) { + /* Remove job from queue. */ + if (mali_pp_job_is_virtual(job)) { + MALI_DEBUG_ASSERT(1 == mali_pp_job_get_sub_job_count(job)); + if (0 == mali_pp_job_get_first_unstarted_sub_job(job)) { + --virtual_job_queue.depth; + } + } else { + job_queue.depth -= mali_pp_job_get_sub_job_count(job) - mali_pp_job_get_first_unstarted_sub_job(job); + } + + _mali_osk_list_delinit(&job->list); + _mali_osk_list_delinit(&job->session_fb_lookup_list); + + mali_pp_job_mark_unstarted_failed(job); + + if (mali_pp_job_is_complete(job)) { + /* Job is complete, remove from session list. */ + _mali_osk_list_delinit(&job->session_list); + + /* Move job to local list for release and deletion. */ + _mali_osk_list_add(&job->list, &removed_jobs); + + MALI_DEBUG_PRINT(3, ("Mali PP scheduler: Aborted PP job %u (0x%08X).\n", mali_pp_job_get_id(job), job)); + } else { + MALI_DEBUG_PRINT(3, ("Mali PP scheduler: Keeping partially started PP job %u (0x%08X) in session.\n", mali_pp_job_get_id(job), job)); + } + } + + _MALI_OSK_LIST_FOREACHENTRY(group, tmp_group, &group_list_working, struct mali_group, pp_scheduler_list) { + groups[i++] = group; + } + + _MALI_OSK_LIST_FOREACHENTRY(group, tmp_group, &group_list_idle, struct mali_group, pp_scheduler_list) { + groups[i++] = group; + } + + mali_pp_scheduler_unlock(); + + /* Release and delete all found jobs from the aborting session. */ + _MALI_OSK_LIST_FOREACHENTRY(job, tmp_job, &removed_jobs, struct mali_pp_job, list) { + mali_timeline_tracker_release(&job->tracker); + mali_pp_job_delete(job); + mali_pp_scheduler_job_completed(); + } + + /* Abort any running jobs from the session. */ + while (i > 0) { + mali_group_abort_session(groups[--i], session); + } + + if (mali_pp_scheduler_has_virtual_group()) { + mali_group_abort_session(virtual_group, session); + } +} + +static mali_bool mali_pp_scheduler_is_suspended(void *data) +{ + mali_bool ret; + + /* This callback does not use the data pointer. */ + MALI_IGNORE(data); + + mali_pp_scheduler_lock(); + + ret = pause_count > 0 + && _mali_osk_list_empty(&group_list_working) + && VIRTUAL_GROUP_WORKING != virtual_group_state; + + mali_pp_scheduler_unlock(); + + return ret; +} + +struct mali_pp_core *mali_pp_scheduler_get_virtual_pp(void) +{ + if (mali_pp_scheduler_has_virtual_group()) { + return mali_group_get_pp_core(virtual_group); + } else { + return NULL; + } +} + +#if MALI_STATE_TRACKING +u32 mali_pp_scheduler_dump_state(char *buf, u32 size) +{ + int n = 0; + struct mali_group *group; + struct mali_group *temp; + + n += _mali_osk_snprintf(buf + n, size - n, "PP:\n"); + n += _mali_osk_snprintf(buf + n, size - n, "\tQueue is %s\n", _mali_osk_list_empty(&job_queue.normal_pri) ? "empty" : "not empty"); + n += _mali_osk_snprintf(buf + n, size - n, "\tHigh priority queue is %s\n", _mali_osk_list_empty(&job_queue.high_pri) ? "empty" : "not empty"); + n += _mali_osk_snprintf(buf + n, size - n, "\n"); + + _MALI_OSK_LIST_FOREACHENTRY(group, temp, &group_list_working, struct mali_group, pp_scheduler_list) { + n += mali_group_dump_state(group, buf + n, size - n); + } + + _MALI_OSK_LIST_FOREACHENTRY(group, temp, &group_list_idle, struct mali_group, pp_scheduler_list) { + n += mali_group_dump_state(group, buf + n, size - n); + } + + _MALI_OSK_LIST_FOREACHENTRY(group, temp, &group_list_disabled, struct mali_group, pp_scheduler_list) { + n += mali_group_dump_state(group, buf + n, size - n); + } + + if (mali_pp_scheduler_has_virtual_group()) { + n += mali_group_dump_state(virtual_group, buf + n, size -n); + } + + n += _mali_osk_snprintf(buf + n, size - n, "\n"); + return n; +} +#endif + +/* This function is intended for power on reset of all cores. + * No locking is done for the list iteration, which can only be safe if the + * scheduler is paused and all cores idle. That is always the case on init and + * power on. */ +void mali_pp_scheduler_reset_all_groups(void) +{ + struct mali_group *group, *temp; + struct mali_group *groups[MALI_MAX_NUMBER_OF_GROUPS]; + s32 i = 0; + + if (mali_pp_scheduler_has_virtual_group()) { + mali_group_lock(virtual_group); + mali_group_reset(virtual_group); + mali_group_unlock(virtual_group); + } + + MALI_DEBUG_ASSERT(_mali_osk_list_empty(&group_list_working)); + MALI_DEBUG_ASSERT(VIRTUAL_GROUP_WORKING != virtual_group_state); + mali_pp_scheduler_lock(); + _MALI_OSK_LIST_FOREACHENTRY(group, temp, &group_list_idle, struct mali_group, pp_scheduler_list) { + groups[i++] = group; + } + mali_pp_scheduler_unlock(); + + while (i > 0) { + group = groups[--i]; + + mali_group_lock(group); + mali_group_reset(group); + mali_group_unlock(group); + } +} + +void mali_pp_scheduler_zap_all_active(struct mali_session_data *session) +{ + struct mali_group *group, *temp; + struct mali_group *groups[MALI_MAX_NUMBER_OF_GROUPS]; + s32 i = 0; + + if (mali_pp_scheduler_has_virtual_group()) { + mali_group_zap_session(virtual_group, session); + } + + mali_pp_scheduler_lock(); + _MALI_OSK_LIST_FOREACHENTRY(group, temp, &group_list_working, struct mali_group, pp_scheduler_list) { + groups[i++] = group; + } + mali_pp_scheduler_unlock(); + + while (i > 0) { + mali_group_zap_session(groups[--i], session); + } +} + +/* A pm reference must be taken with _mali_osk_pm_dev_ref_add_no_power_on + * before calling this function to avoid Mali powering down as HW is accessed. + */ +static void mali_pp_scheduler_enable_group_internal(struct mali_group *group) +{ + MALI_DEBUG_ASSERT_POINTER(group); + + mali_group_lock(group); + + if (MALI_GROUP_STATE_DISABLED != group->state) { + mali_group_unlock(group); + MALI_DEBUG_PRINT(4, ("Mali PP scheduler: PP group %p already enabled.\n", group)); + return; + } + + MALI_DEBUG_PRINT(3, ("Mali PP scheduler: Enabling PP group %p.\n", group)); + + mali_pp_scheduler_lock(); + + MALI_DEBUG_ASSERT(MALI_GROUP_STATE_DISABLED == group->state); + ++enabled_cores; + + if (mali_pp_scheduler_has_virtual_group()) { + mali_bool update_hw; + + /* Add group to virtual group. */ + _mali_osk_list_delinit(&(group->pp_scheduler_list)); + group->state = MALI_GROUP_STATE_JOINING_VIRTUAL; + + mali_pp_scheduler_unlock(); + mali_group_unlock(group); + + mali_group_lock(virtual_group); + + update_hw = mali_pm_is_power_on(); + /* Get ref of group domain */ + mali_group_get_pm_domain_ref(group); + + MALI_DEBUG_ASSERT(NULL == group->pm_domain || + MALI_PM_DOMAIN_ON == mali_pm_domain_state_get(group->pm_domain)); + + if (update_hw) { + mali_group_lock(group); + mali_group_power_on_group(group); + mali_group_reset(group); + mali_group_unlock(group); + } + + mali_pp_scheduler_enable_empty_virtual(); + mali_group_add_group(virtual_group, group, update_hw); + MALI_DEBUG_PRINT(4, ("Mali PP scheduler: Done enabling group %p. Added to virtual group.\n", group)); + + mali_group_unlock(virtual_group); + } else { + /* Get ref of group domain */ + mali_group_get_pm_domain_ref(group); + + MALI_DEBUG_ASSERT(NULL == group->pm_domain || + MALI_PM_DOMAIN_ON == mali_pm_domain_state_get(group->pm_domain)); + + /* Put group on idle list. */ + if (mali_pm_is_power_on()) { + mali_group_power_on_group(group); + mali_group_reset(group); + } + + _mali_osk_list_move(&(group->pp_scheduler_list), &group_list_idle); + group->state = MALI_GROUP_STATE_IDLE; + + MALI_DEBUG_PRINT(4, ("Mali PP scheduler: Done enabling group %p. Now on idle list.\n", group)); + mali_pp_scheduler_unlock(); + mali_group_unlock(group); + } +} + +void mali_pp_scheduler_enable_group(struct mali_group *group) +{ + MALI_DEBUG_ASSERT_POINTER(group); + + _mali_osk_pm_dev_ref_add_no_power_on(); + + mali_pp_scheduler_enable_group_internal(group); + + _mali_osk_pm_dev_ref_dec_no_power_on(); + + /* Pick up any jobs that might have been queued if all PP groups were disabled. */ + mali_pp_scheduler_schedule(); +} + +static void mali_pp_scheduler_disable_group_internal(struct mali_group *group) +{ + if (mali_pp_scheduler_has_virtual_group()) { + mali_group_lock(virtual_group); + + MALI_DEBUG_ASSERT(VIRTUAL_GROUP_WORKING != virtual_group_state); + if (MALI_GROUP_STATE_JOINING_VIRTUAL == group->state) { + /* The group was in the process of being added to the virtual group. We + * only need to change the state to reverse this. */ + group->state = MALI_GROUP_STATE_LEAVING_VIRTUAL; + } else if (MALI_GROUP_STATE_IN_VIRTUAL == group->state) { + /* Remove group from virtual group. The state of the group will be + * LEAVING_VIRTUAL and the group will not be on any scheduler list. */ + mali_group_remove_group(virtual_group, group); + + mali_pp_scheduler_disable_empty_virtual(); + } + + mali_group_unlock(virtual_group); + } + + mali_group_lock(group); + mali_pp_scheduler_lock(); + + MALI_DEBUG_ASSERT( MALI_GROUP_STATE_IDLE == group->state + || MALI_GROUP_STATE_LEAVING_VIRTUAL == group->state + || MALI_GROUP_STATE_DISABLED == group->state); + + if (MALI_GROUP_STATE_DISABLED == group->state) { + MALI_DEBUG_PRINT(4, ("Mali PP scheduler: PP group %p already disabled.\n", group)); + } else { + MALI_DEBUG_PRINT(3, ("Mali PP scheduler: Disabling PP group %p.\n", group)); + + --enabled_cores; + _mali_osk_list_move(&(group->pp_scheduler_list), &group_list_disabled); + group->state = MALI_GROUP_STATE_DISABLED; + + mali_group_power_off_group(group, MALI_TRUE); + mali_group_put_pm_domain_ref(group); + } + + mali_pp_scheduler_unlock(); + mali_group_unlock(group); +} + +void mali_pp_scheduler_disable_group(struct mali_group *group) +{ + MALI_DEBUG_ASSERT_POINTER(group); + + mali_pp_scheduler_suspend(); + + _mali_osk_pm_dev_ref_add_no_power_on(); + + mali_pp_scheduler_disable_group_internal(group); + + _mali_osk_pm_dev_ref_dec_no_power_on(); + + mali_pp_scheduler_resume(); +} + +static void mali_pp_scheduler_notify_core_change(u32 num_cores) +{ + mali_bool done = MALI_FALSE; + + if (mali_is_mali450()) { + return; + } + + /* + * This function gets a bit complicated because we can't hold the session lock while + * allocating notification objects. + */ + + while (!done) { + u32 i; + u32 num_sessions_alloc; + u32 num_sessions_with_lock; + u32 used_notification_objects = 0; + _mali_osk_notification_t **notobjs; + + /* Pre allocate the number of notifications objects we need right now (might change after lock has been taken) */ + num_sessions_alloc = mali_session_get_count(); + if (0 == num_sessions_alloc) { + /* No sessions to report to */ + return; + } + + notobjs = (_mali_osk_notification_t **)_mali_osk_malloc(sizeof(_mali_osk_notification_t *) * num_sessions_alloc); + if (NULL == notobjs) { + MALI_PRINT_ERROR(("Failed to notify user space session about num PP core change (alloc failure)\n")); + /* there is probably no point in trying again, system must be really low on memory and probably unusable now anyway */ + return; + } + + for (i = 0; i < num_sessions_alloc; i++) { + notobjs[i] = _mali_osk_notification_create(_MALI_NOTIFICATION_PP_NUM_CORE_CHANGE, sizeof(_mali_uk_pp_num_cores_changed_s)); + if (NULL != notobjs[i]) { + _mali_uk_pp_num_cores_changed_s *data = notobjs[i]->result_buffer; + data->number_of_enabled_cores = num_cores; + } else { + MALI_PRINT_ERROR(("Failed to notify user space session about num PP core change (alloc failure %u)\n", i)); + } + } + + mali_session_lock(); + + /* number of sessions will not change while we hold the lock */ + num_sessions_with_lock = mali_session_get_count(); + + if (num_sessions_alloc >= num_sessions_with_lock) { + /* We have allocated enough notification objects for all the sessions atm */ + struct mali_session_data *session, *tmp; + MALI_SESSION_FOREACH(session, tmp, link) { + MALI_DEBUG_ASSERT(used_notification_objects < num_sessions_alloc); + if (NULL != notobjs[used_notification_objects]) { + mali_session_send_notification(session, notobjs[used_notification_objects]); + notobjs[used_notification_objects] = NULL; /* Don't track this notification object any more */ + } + used_notification_objects++; + } + done = MALI_TRUE; + } + + mali_session_unlock(); + + /* Delete any remaining/unused notification objects */ + for (; used_notification_objects < num_sessions_alloc; used_notification_objects++) { + if (NULL != notobjs[used_notification_objects]) { + _mali_osk_notification_delete(notobjs[used_notification_objects]); + } + } + + _mali_osk_free(notobjs); + } +} + +static void mali_pp_scheduler_core_scale_up(unsigned int target_core_nr) +{ + MALI_DEBUG_PRINT(2, ("Requesting %d cores: enabling %d cores\n", target_core_nr, target_core_nr - enabled_cores)); + + _mali_osk_pm_dev_ref_add_no_power_on(); + _mali_osk_pm_dev_barrier(); + + while (target_core_nr > enabled_cores) { + /* + * If there are any cores which do not belong to any domain, + * then these will always be found at the head of the list and + * we'll thus enabled these first. + */ + + mali_pp_scheduler_lock(); + + if (!_mali_osk_list_empty(&group_list_disabled)) { + struct mali_group *group; + + group = _MALI_OSK_LIST_ENTRY(group_list_disabled.next, struct mali_group, pp_scheduler_list); + + MALI_DEBUG_ASSERT_POINTER(group); + MALI_DEBUG_ASSERT(MALI_GROUP_STATE_DISABLED == group->state); + + mali_pp_scheduler_unlock(); + + mali_pp_scheduler_enable_group_internal(group); + } else { + mali_pp_scheduler_unlock(); + break; /* no more groups on disabled list */ + } + } + + _mali_osk_pm_dev_ref_dec_no_power_on(); + + mali_pp_scheduler_schedule(); +} + +static void mali_pp_scheduler_core_scale_down(unsigned int target_core_nr) +{ + MALI_DEBUG_PRINT(2, ("Requesting %d cores: disabling %d cores\n", target_core_nr, enabled_cores - target_core_nr)); + + mali_pp_scheduler_suspend(); + + MALI_DEBUG_ASSERT(_mali_osk_list_empty(&group_list_working)); + + _mali_osk_pm_dev_ref_add_no_power_on(); + + if (NULL != mali_pmu_get_global_pmu_core()) { + int i; + + for (i = MALI_MAX_NUMBER_OF_DOMAINS - 1; i >= 0; i--) { + if (target_core_nr < enabled_cores) { + struct mali_pm_domain *domain; + + domain = mali_pm_domain_get_from_index(i); + + /* Domain is valid and has pp cores */ + if ((NULL != domain) && (NULL != domain->group_list)) { + struct mali_group *group; + + MALI_PM_DOMAIN_FOR_EACH_GROUP(group, domain) { + /* If group is pp core */ + if (NULL != mali_group_get_pp_core(group)) { + mali_pp_scheduler_disable_group_internal(group); + if (target_core_nr >= enabled_cores) { + break; + } + } + } + } + } else { + break; + } + } + } + + /* + * Didn't find enough cores associated with a power domain, + * so we need to disable cores which we can't power off with the PMU. + * Start with physical groups used by the scheduler, + * then remove physical from virtual if even more groups are needed. + */ + + while (target_core_nr < enabled_cores) { + mali_pp_scheduler_lock(); + if (!_mali_osk_list_empty(&group_list_idle)) { + struct mali_group *group; + + group = _MALI_OSK_LIST_ENTRY(group_list_idle.next, struct mali_group, pp_scheduler_list); + MALI_DEBUG_ASSERT_POINTER(group); + + mali_pp_scheduler_unlock(); + + mali_pp_scheduler_disable_group_internal(group); + } else { + mali_pp_scheduler_unlock(); + break; /* No more physical groups */ + } + } + + if (mali_pp_scheduler_has_virtual_group()) { + while (target_core_nr < enabled_cores) { + mali_group_lock(virtual_group); + if (!_mali_osk_list_empty(&virtual_group->group_list)) { + struct mali_group *group; + + group = _MALI_OSK_LIST_ENTRY(virtual_group->group_list.next, struct mali_group, group_list); + MALI_DEBUG_ASSERT_POINTER(group); + + mali_group_unlock(virtual_group); + + mali_pp_scheduler_disable_group_internal(group); + } else { + mali_group_unlock(virtual_group); + break; /* No more physical groups in virtual group */ + } + } + } + + _mali_osk_pm_dev_ref_dec_no_power_on(); + + mali_pp_scheduler_resume(); +} + +int mali_pp_scheduler_set_perf_level(unsigned int target_core_nr, mali_bool override) +{ + if (target_core_nr == enabled_cores) return 0; + if (MALI_FALSE == core_scaling_enabled && MALI_FALSE == override) return -EPERM; + if (target_core_nr > num_cores) return -EINVAL; + if (0 == target_core_nr) return -EINVAL; + + if (target_core_nr > enabled_cores) { + mali_pp_scheduler_core_scale_up(target_core_nr); + } else if (target_core_nr < enabled_cores) { + mali_pp_scheduler_core_scale_down(target_core_nr); + } + + if (target_core_nr != enabled_cores) { + MALI_DEBUG_PRINT(2, ("Core scaling failed, target number: %d, actual number: %d\n", target_core_nr, enabled_cores)); + } + + mali_pp_scheduler_notify_core_change(enabled_cores); + + return 0; +} + +void mali_pp_scheduler_core_scaling_enable(void) +{ + /* PS: Core scaling is by default enabled */ + core_scaling_enabled = MALI_TRUE; +} + +void mali_pp_scheduler_core_scaling_disable(void) +{ + core_scaling_enabled = MALI_FALSE; +} + +mali_bool mali_pp_scheduler_core_scaling_is_enabled(void) +{ + return core_scaling_enabled; +} + +static void mali_pp_scheduler_job_queued(void) +{ + /* We hold a PM reference for every job we hold queued (and running) */ + _mali_osk_pm_dev_ref_add(); + + if (mali_utilization_enabled()) { + /* + * We cheat a little bit by counting the PP as busy from the time a PP job is queued. + * This will be fine because we only loose the tiny idle gap between jobs, but + * we will instead get less utilization work to do (less locks taken) + */ + mali_utilization_pp_start(); + } +} + +static void mali_pp_scheduler_job_completed(void) +{ + /* Release the PM reference we got in the mali_pp_scheduler_job_queued() function */ + _mali_osk_pm_dev_ref_dec(); + + if (mali_utilization_enabled()) { + mali_utilization_pp_end(); + } +} + +static void mali_pp_scheduler_abort_job_and_unlock_scheduler(struct mali_pp_job *job) +{ + MALI_DEBUG_ASSERT_POINTER(job); + MALI_DEBUG_ASSERT_LOCK_HELD(pp_scheduler_lock); + + /* This job should not be on any lists. */ + MALI_DEBUG_ASSERT(_mali_osk_list_empty(&job->list)); + MALI_DEBUG_ASSERT(_mali_osk_list_empty(&job->session_list)); + + _mali_osk_list_delinit(&job->session_fb_lookup_list); + + mali_pp_scheduler_unlock(); + + /* Release tracker. */ + mali_timeline_tracker_release(&job->tracker); +} + +static mali_scheduler_mask mali_pp_scheduler_queue_job(struct mali_pp_job *job) +{ + _mali_osk_list_t *queue = NULL; + mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY; + struct mali_pp_job *iter, *tmp; + + MALI_DEBUG_ASSERT_POINTER(job); + MALI_DEBUG_ASSERT_POINTER(job->session); + +#if defined(MALI_PP_SCHEDULER_USE_DEFERRED_JOB_QUEUE) + if (mali_pp_job_needs_dma_buf_mapping(job)) { + mali_dma_buf_map_job(job); + } +#endif /* defined(MALI_PP_SCHEDULER_USE_DEFERRED_JOB_QUEUE) */ + + mali_pp_scheduler_lock(); + + if (unlikely(job->session->is_aborting)) { + /* Before checking if the session is aborting, the scheduler must be locked. */ + MALI_DEBUG_ASSERT_LOCK_HELD(pp_scheduler_lock); + + MALI_DEBUG_PRINT(2, ("Mali PP scheduler: Job %u (0x%08X) queued while session is aborting.\n", mali_pp_job_get_id(job), job)); + + mali_pp_scheduler_abort_job_and_unlock_scheduler(job); + + /* Delete job. */ +#if defined(MALI_PP_SCHEDULER_USE_DEFERRED_JOB_DELETE) + mali_pp_scheduler_deferred_job_delete(job); +#else + mali_pp_job_delete(job); +#endif /* defined(MALI_PP_SCHEDULER_USE_DEFERRED_JOB_DELETE) */ + mali_pp_scheduler_job_completed(); + + /* Since we are aborting we ignore the scheduler mask. */ + return MALI_SCHEDULER_MASK_EMPTY; + } + +#if defined(CONFIG_GPU_TRACEPOINTS) && defined(CONFIG_TRACEPOINTS) + trace_gpu_job_enqueue(mali_pp_job_get_tid(job), mali_pp_job_get_id(job), "PP"); +#endif + + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_SINGLE | MALI_PROFILING_EVENT_CHANNEL_SOFTWARE | MALI_PROFILING_EVENT_REASON_SINGLE_SW_PP_ENQUEUE, job->pid, job->tid, job->uargs.frame_builder_id, job->uargs.flush_id, 0); + + job->cache_order = mali_scheduler_get_new_cache_order(); + + /* Determine which queue the job should be added to. */ + if (mali_pp_job_is_virtual(job)) { + if (job->session->use_high_priority_job_queue) { + queue = &virtual_job_queue.high_pri; + } else { + queue = &virtual_job_queue.normal_pri; + } + + virtual_job_queue.depth += 1; + + /* Set schedule bitmask if the virtual group is idle. */ + if (VIRTUAL_GROUP_IDLE == virtual_group_state) { + schedule_mask |= MALI_SCHEDULER_MASK_PP; + } + } else { + if (job->session->use_high_priority_job_queue) { + queue = &job_queue.high_pri; + } else { + queue = &job_queue.normal_pri; + } + + job_queue.depth += mali_pp_job_get_sub_job_count(job); + + /* Set schedule bitmask if there are physical PP cores available, or if there is an + * idle virtual group. */ + if (!_mali_osk_list_empty(&group_list_idle) + || (mali_pp_scheduler_has_virtual_group() + && (VIRTUAL_GROUP_IDLE == virtual_group_state))) { + schedule_mask |= MALI_SCHEDULER_MASK_PP; + } + } + + /* Find position in queue where job should be added. */ + _MALI_OSK_LIST_FOREACHENTRY_REVERSE(iter, tmp, queue, struct mali_pp_job, list) { + if (mali_pp_job_should_start_after(job, iter)) { + break; + } + } + + /* Add job to queue. */ + _mali_osk_list_add(&job->list, &iter->list); + + /* Add job to session list. */ + _mali_osk_list_addtail(&job->session_list, &(job->session->pp_job_list)); + + MALI_DEBUG_PRINT(3, ("Mali PP scheduler: %s job %u (0x%08X) with %u parts queued.\n", + mali_pp_job_is_virtual(job) ? "Virtual" : "Physical", + mali_pp_job_get_id(job), job, mali_pp_job_get_sub_job_count(job))); + + mali_pp_scheduler_unlock(); + + return schedule_mask; +} + +mali_scheduler_mask mali_pp_scheduler_activate_job(struct mali_pp_job *job) +{ + mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY; + + MALI_DEBUG_ASSERT_POINTER(job); + MALI_DEBUG_ASSERT_POINTER(job->session); + + MALI_DEBUG_PRINT(4, ("Mali PP scheduler: Timeline activation for job %u (0x%08X).\n", mali_pp_job_get_id(job), job)); + + if (MALI_TIMELINE_ACTIVATION_ERROR_FATAL_BIT & job->tracker.activation_error) { + MALI_DEBUG_PRINT(2, ("Mali PP scheduler: Job %u (0x%08X) activated with error, aborting.\n", mali_pp_job_get_id(job), job)); + + mali_pp_scheduler_lock(); + mali_pp_scheduler_abort_job_and_unlock_scheduler(job); + + mali_pp_job_mark_sub_job_completed(job, MALI_FALSE); /* Flagging the job as failed. */ + mali_pp_scheduler_finalize_job(job); + + return MALI_SCHEDULER_MASK_EMPTY; + } + + /* PP job is ready to run, queue it. */ + +#if defined(MALI_PP_SCHEDULER_USE_DEFERRED_JOB_QUEUE) + if (mali_pp_job_needs_dma_buf_mapping(job)) { + mali_pp_scheduler_deferred_job_queue(job); + + return MALI_SCHEDULER_MASK_EMPTY; + } +#endif /* defined(MALI_PP_SCHEDULER_USE_DEFERRED_JOB_QUEUE) */ + + schedule_mask = mali_pp_scheduler_queue_job(job); + + return schedule_mask; +} diff --git a/drivers/gpu/arm/mali/common/mali_pp_scheduler.h b/drivers/gpu/arm/mali/common/mali_pp_scheduler.h new file mode 100644 index 00000000000000..e414dff6731e10 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_pp_scheduler.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_PP_SCHEDULER_H__ +#define __MALI_PP_SCHEDULER_H__ + +#include "mali_osk.h" +#include "mali_pp_job.h" +#include "mali_group.h" +#include "linux/mali/mali_utgard.h" + +/** Initalize the HW independent parts of the PP scheduler + */ +_mali_osk_errcode_t mali_pp_scheduler_initialize(void); +void mali_pp_scheduler_terminate(void); + +/** Poplulate the PP scheduler with groups + */ +void mali_pp_scheduler_populate(void); +void mali_pp_scheduler_depopulate(void); + +/** + * @brief Handle job completion. + * + * Will attempt to start a new job on the locked group. + * + * If all sub jobs have completed the job's tracker will be released, any other resources associated + * with the job will be freed. A notification will also be sent to user space. + * + * Releasing the tracker might activate other jobs, so if appropriate we also schedule them. + * + * @note Group must be locked when entering this function. Will be unlocked before exiting. + * + * @param group The group that completed the job. + * @param job The job that is done. + * @param sub_job Sub job of job. + * @param success MALI_TRUE if job completed successfully, MALI_FALSE if not. + * @param in_upper_half MALI_TRUE if called from upper half, MALI_FALSE if not. + */ +void mali_pp_scheduler_job_done(struct mali_group *group, struct mali_pp_job *job, u32 sub_job, mali_bool success, mali_bool in_upper_half); + +void mali_pp_scheduler_suspend(void); +void mali_pp_scheduler_resume(void); + +/** + * @brief Abort all running and queued PP jobs from session. + * + * This functions aborts all PP jobs from the specified session. Queued jobs are removed from the + * queue and jobs currently running on a core will be aborted. + * + * @param session Session that is aborting. + */ +void mali_pp_scheduler_abort_session(struct mali_session_data *session); + +/** + * @brief Reset all groups + * + * This function resets all groups known by the PP scheuduler. This must be + * called after the Mali HW has been powered on in order to reset the HW. + * + * This function is intended for power on reset of all cores. + * No locking is done, which can only be safe if the scheduler is paused and + * all cores idle. That is always the case on init and power on. + */ +void mali_pp_scheduler_reset_all_groups(void); + +/** + * @brief Zap TLB on all groups with \a session active + * + * The scheculer will zap the session on all groups it owns. + */ +void mali_pp_scheduler_zap_all_active(struct mali_session_data *session); + +/** + * @brief Get the virtual PP core + * + * The returned PP core may only be used to prepare DMA command buffers for the + * PP core. Other actions must go through the PP scheduler, or the virtual + * group. + * + * @return Pointer to the virtual PP core, NULL if this doesn't exist + */ +struct mali_pp_core *mali_pp_scheduler_get_virtual_pp(void); + +u32 mali_pp_scheduler_dump_state(char *buf, u32 size); + +void mali_pp_scheduler_enable_group(struct mali_group *group); +void mali_pp_scheduler_disable_group(struct mali_group *group); + +/** + * @brief Used by the Timeline system to queue a PP job. + * + * @note @ref mali_scheduler_schedule_from_mask() should be called if this function returns non-zero. + * + * @param job The PP job that is being activated. + * + * @return A scheduling bitmask that can be used to decide if scheduling is necessary after this + * call. + */ +mali_scheduler_mask mali_pp_scheduler_activate_job(struct mali_pp_job *job); + +/** + * @brief Schedule queued jobs on idle cores. + */ +void mali_pp_scheduler_schedule(void); + +int mali_pp_scheduler_set_perf_level(u32 cores, mali_bool override); + +void mali_pp_scheduler_core_scaling_enable(void); +void mali_pp_scheduler_core_scaling_disable(void); +mali_bool mali_pp_scheduler_core_scaling_is_enabled(void); + +u32 mali_pp_scheduler_get_num_cores_total(void); +u32 mali_pp_scheduler_get_num_cores_enabled(void); + +/** + * @brief Returns the number of Pixel Processors in the system irrespective of the context + * + * @return number of physical Pixel Processor cores in the system + */ +u32 mali_pp_scheduler_get_num_cores_total(void); + +#endif /* __MALI_PP_SCHEDULER_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_scheduler.c b/drivers/gpu/arm/mali/common/mali_scheduler.c new file mode 100644 index 00000000000000..1e070ce8bc0d81 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_scheduler.c @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_scheduler.h" + +#include "mali_kernel_common.h" +#include "mali_osk.h" + +mali_bool mali_scheduler_hints[MALI_SCHEDULER_HINT_MAX]; + +static _mali_osk_atomic_t mali_job_id_autonumber; +static _mali_osk_atomic_t mali_job_cache_order_autonumber; + +static _mali_osk_wq_work_t *pp_scheduler_wq_high_pri = NULL; +static _mali_osk_wq_work_t *gp_scheduler_wq_high_pri = NULL; + +static void mali_scheduler_wq_schedule_pp(void *arg) +{ + MALI_IGNORE(arg); + + mali_pp_scheduler_schedule(); +} + +static void mali_scheduler_wq_schedule_gp(void *arg) +{ + MALI_IGNORE(arg); + + mali_gp_scheduler_schedule(); +} + +_mali_osk_errcode_t mali_scheduler_initialize(void) +{ + if ( _MALI_OSK_ERR_OK != _mali_osk_atomic_init(&mali_job_id_autonumber, 0)) { + MALI_DEBUG_PRINT(1, ("Initialization of atomic job id counter failed.\n")); + return _MALI_OSK_ERR_FAULT; + } + + if ( _MALI_OSK_ERR_OK != _mali_osk_atomic_init(&mali_job_cache_order_autonumber, 0)) { + MALI_DEBUG_PRINT(1, ("Initialization of atomic job cache order counter failed.\n")); + _mali_osk_atomic_term(&mali_job_id_autonumber); + return _MALI_OSK_ERR_FAULT; + } + + pp_scheduler_wq_high_pri = _mali_osk_wq_create_work_high_pri(mali_scheduler_wq_schedule_pp, NULL); + if (NULL == pp_scheduler_wq_high_pri) { + _mali_osk_atomic_term(&mali_job_cache_order_autonumber); + _mali_osk_atomic_term(&mali_job_id_autonumber); + return _MALI_OSK_ERR_NOMEM; + } + + gp_scheduler_wq_high_pri = _mali_osk_wq_create_work_high_pri(mali_scheduler_wq_schedule_gp, NULL); + if (NULL == gp_scheduler_wq_high_pri) { + _mali_osk_wq_delete_work(pp_scheduler_wq_high_pri); + _mali_osk_atomic_term(&mali_job_cache_order_autonumber); + _mali_osk_atomic_term(&mali_job_id_autonumber); + return _MALI_OSK_ERR_NOMEM; + } + + return _MALI_OSK_ERR_OK; +} + +void mali_scheduler_terminate(void) +{ + _mali_osk_wq_delete_work(gp_scheduler_wq_high_pri); + _mali_osk_wq_delete_work(pp_scheduler_wq_high_pri); + _mali_osk_atomic_term(&mali_job_cache_order_autonumber); + _mali_osk_atomic_term(&mali_job_id_autonumber); +} + +u32 mali_scheduler_get_new_id(void) +{ + u32 job_id = _mali_osk_atomic_inc_return(&mali_job_id_autonumber); + return job_id; +} + +u32 mali_scheduler_get_new_cache_order(void) +{ + u32 job_cache_order = _mali_osk_atomic_inc_return(&mali_job_cache_order_autonumber); + return job_cache_order; +} + +void mali_scheduler_schedule_from_mask(mali_scheduler_mask mask, mali_bool deferred_schedule) +{ + if (MALI_SCHEDULER_MASK_GP & mask) { + /* GP needs scheduling. */ + if (deferred_schedule) { + /* Schedule GP deferred. */ + _mali_osk_wq_schedule_work_high_pri(gp_scheduler_wq_high_pri); + } else { + /* Schedule GP now. */ + mali_gp_scheduler_schedule(); + } + } + + if (MALI_SCHEDULER_MASK_PP & mask) { + /* PP needs scheduling. */ + if (deferred_schedule) { + /* Schedule PP deferred. */ + _mali_osk_wq_schedule_work_high_pri(pp_scheduler_wq_high_pri); + } else { + /* Schedule PP now. */ + mali_pp_scheduler_schedule(); + } + } +} diff --git a/drivers/gpu/arm/mali/common/mali_scheduler.h b/drivers/gpu/arm/mali/common/mali_scheduler.h new file mode 100644 index 00000000000000..73860531569604 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_scheduler.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_SCHEDULER_H__ +#define __MALI_SCHEDULER_H__ + +#include "mali_osk.h" +#include "mali_scheduler_types.h" +#include "mali_gp_scheduler.h" +#include "mali_pp_scheduler.h" + +_mali_osk_errcode_t mali_scheduler_initialize(void); +void mali_scheduler_terminate(void); + +u32 mali_scheduler_get_new_id(void); +u32 mali_scheduler_get_new_cache_order(void); + +/** + * @brief Reset all groups + * + * This function resets all groups known by the both the PP and GP scheuduler. + * This must be called after the Mali HW has been powered on in order to reset + * the HW. + */ +MALI_STATIC_INLINE void mali_scheduler_reset_all_groups(void) +{ + mali_gp_scheduler_reset_all_groups(); + mali_pp_scheduler_reset_all_groups(); +} + +/** + * @brief Zap TLB on all active groups running \a session + * + * @param session Pointer to the session to zap + */ +MALI_STATIC_INLINE void mali_scheduler_zap_all_active(struct mali_session_data *session) +{ + mali_gp_scheduler_zap_all_active(session); + mali_pp_scheduler_zap_all_active(session); +} + +/** + * Check if bit is set in scheduler mask. + * + * @param mask Scheduler mask to check. + * @param bit Bit to check. + * @return MALI_TRUE if bit is set in scheduler mask, MALI_FALSE if not. + */ +MALI_STATIC_INLINE mali_bool mali_scheduler_mask_is_set(mali_scheduler_mask mask, mali_scheduler_mask bit) +{ + return MALI_SCHEDULER_MASK_EMPTY != (bit & mask); +} + +/** + * Schedule GP and PP according to bitmask. + * + * @param mask A scheduling bitmask. + * @param deferred_schedule MALI_TRUE if schedule should be deferred, MALI_FALSE if not. + */ +void mali_scheduler_schedule_from_mask(mali_scheduler_mask mask, mali_bool deferred_schedule); + +/* Enable or disable scheduler hint. */ +extern mali_bool mali_scheduler_hints[MALI_SCHEDULER_HINT_MAX]; + +MALI_STATIC_INLINE void mali_scheduler_hint_enable(mali_scheduler_hint hint) +{ + MALI_DEBUG_ASSERT(hint < MALI_SCHEDULER_HINT_MAX); + mali_scheduler_hints[hint] = MALI_TRUE; +} + +MALI_STATIC_INLINE void mali_scheduler_hint_disable(mali_scheduler_hint hint) +{ + MALI_DEBUG_ASSERT(hint < MALI_SCHEDULER_HINT_MAX); + mali_scheduler_hints[hint] = MALI_FALSE; +} + +MALI_STATIC_INLINE mali_bool mali_scheduler_hint_is_enabled(mali_scheduler_hint hint) +{ + MALI_DEBUG_ASSERT(hint < MALI_SCHEDULER_HINT_MAX); + return mali_scheduler_hints[hint]; +} + +#endif /* __MALI_SCHEDULER_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_scheduler_types.h b/drivers/gpu/arm/mali/common/mali_scheduler_types.h new file mode 100644 index 00000000000000..02490c20a39f3b --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_scheduler_types.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_SCHEDULER_TYPES_H__ +#define __MALI_SCHEDULER_TYPES_H__ + +#include "mali_osk.h" + +#define MALI_SCHEDULER_JOB_ID_SPAN 65535 + +/** + * Bitmask used for defered scheduling of subsystems. + */ +typedef u32 mali_scheduler_mask; + +#define MALI_SCHEDULER_MASK_GP (1<<0) +#define MALI_SCHEDULER_MASK_PP (1<<1) + +#define MALI_SCHEDULER_MASK_EMPTY 0 +#define MALI_SCHEDULER_MASK_ALL (MALI_SCHEDULER_MASK_GP | MALI_SCHEDULER_MASK_PP) + +typedef enum { + MALI_SCHEDULER_HINT_GP_BOUND = 0 +#define MALI_SCHEDULER_HINT_MAX 1 +} mali_scheduler_hint; + +#endif /* __MALI_SCHEDULER_TYPES_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_session.c b/drivers/gpu/arm/mali/common/mali_session.c new file mode 100644 index 00000000000000..812dbf86364b13 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_session.c @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_osk.h" +#include "mali_osk_list.h" +#include "mali_session.h" + +_MALI_OSK_LIST_HEAD(mali_sessions); +static u32 mali_session_count = 0; + +_mali_osk_spinlock_irq_t *mali_sessions_lock; + +_mali_osk_errcode_t mali_session_initialize(void) +{ + _MALI_OSK_INIT_LIST_HEAD(&mali_sessions); + + mali_sessions_lock = _mali_osk_spinlock_irq_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_SESSIONS); + + if (NULL == mali_sessions_lock) return _MALI_OSK_ERR_NOMEM; + + return _MALI_OSK_ERR_OK; +} + +void mali_session_terminate(void) +{ + _mali_osk_spinlock_irq_term(mali_sessions_lock); +} + +void mali_session_add(struct mali_session_data *session) +{ + mali_session_lock(); + _mali_osk_list_add(&session->link, &mali_sessions); + mali_session_count++; + mali_session_unlock(); +} + +void mali_session_remove(struct mali_session_data *session) +{ + mali_session_lock(); + _mali_osk_list_delinit(&session->link); + mali_session_count--; + mali_session_unlock(); +} + +u32 mali_session_get_count(void) +{ + return mali_session_count; +} + +/* + * Get the max completed window jobs from all active session, + * which will be used in window render frame per sec calculate + */ +#if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY) +u32 mali_session_max_window_num(void) +{ + struct mali_session_data *session, *tmp; + u32 max_window_num = 0; + u32 tmp_number = 0; + + mali_session_lock(); + + MALI_SESSION_FOREACH(session, tmp, link) { + tmp_number = _mali_osk_atomic_xchg(&session->number_of_window_jobs, 0); + if (max_window_num < tmp_number) { + max_window_num = tmp_number; + } + } + + mali_session_unlock(); + + return max_window_num; +} +#endif diff --git a/drivers/gpu/arm/mali/common/mali_session.h b/drivers/gpu/arm/mali/common/mali_session.h new file mode 100644 index 00000000000000..0060406852f150 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_session.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_SESSION_H__ +#define __MALI_SESSION_H__ + +#include "mali_mmu_page_directory.h" +#include "mali_kernel_descriptor_mapping.h" +#include "mali_osk.h" +#include "mali_osk_list.h" + +struct mali_timeline_system; +struct mali_soft_system; + +/* Number of frame builder job lists per session. */ +#define MALI_PP_JOB_FB_LOOKUP_LIST_SIZE 16 +#define MALI_PP_JOB_FB_LOOKUP_LIST_MASK (MALI_PP_JOB_FB_LOOKUP_LIST_SIZE - 1) + +struct mali_session_data { + _mali_osk_notification_queue_t * ioctl_queue; + + _mali_osk_mutex_t *memory_lock; /**< Lock protecting the vm manipulation */ + mali_descriptor_mapping * descriptor_mapping; /**< Mapping between userspace descriptors and our pointers */ + _mali_osk_list_t memory_head; /**< Track all the memory allocated in this session, for freeing on abnormal termination */ + + struct mali_page_directory *page_directory; /**< MMU page directory for this session */ + + _MALI_OSK_LIST_HEAD(link); /**< Link for list of all sessions */ + _MALI_OSK_LIST_HEAD(pp_job_list); /**< List of all PP jobs on this session */ + +#if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY) + _mali_osk_atomic_t number_of_window_jobs; /**< Record the window jobs completed on this session in a period */ +#endif + + _mali_osk_list_t pp_job_fb_lookup_list[MALI_PP_JOB_FB_LOOKUP_LIST_SIZE]; /**< List of PP job lists per frame builder id. Used to link jobs from same frame builder. */ + + struct mali_soft_job_system *soft_job_system; /**< Soft job system for this session. */ + struct mali_timeline_system *timeline_system; /**< Timeline system for this session. */ + + mali_bool is_aborting; /**< MALI_TRUE if the session is aborting, MALI_FALSE if not. */ + mali_bool use_high_priority_job_queue; /**< If MALI_TRUE, jobs added from this session will use the high priority job queues. */ +}; + +_mali_osk_errcode_t mali_session_initialize(void); +void mali_session_terminate(void); + +/* List of all sessions. Actual list head in mali_kernel_core.c */ +extern _mali_osk_list_t mali_sessions; +/* Lock to protect modification and access to the mali_sessions list */ +extern _mali_osk_spinlock_irq_t *mali_sessions_lock; + +MALI_STATIC_INLINE void mali_session_lock(void) +{ + _mali_osk_spinlock_irq_lock(mali_sessions_lock); +} + +MALI_STATIC_INLINE void mali_session_unlock(void) +{ + _mali_osk_spinlock_irq_unlock(mali_sessions_lock); +} + +void mali_session_add(struct mali_session_data *session); +void mali_session_remove(struct mali_session_data *session); +u32 mali_session_get_count(void); + +#define MALI_SESSION_FOREACH(session, tmp, link) \ + _MALI_OSK_LIST_FOREACHENTRY(session, tmp, &mali_sessions, struct mali_session_data, link) + +MALI_STATIC_INLINE struct mali_page_directory *mali_session_get_page_directory(struct mali_session_data *session) +{ + return session->page_directory; +} + +MALI_STATIC_INLINE void mali_session_send_notification(struct mali_session_data *session, _mali_osk_notification_t *object) +{ + _mali_osk_notification_queue_send(session->ioctl_queue, object); +} + +/* + * Get the max completed window jobs from all active session, + * which will be used in window render frame per sec calculate + */ +#if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY) +u32 mali_session_max_window_num(void); +#endif + +#endif /* __MALI_SESSION_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_soft_job.c b/drivers/gpu/arm/mali/common/mali_soft_job.c new file mode 100644 index 00000000000000..e21f4c169788df --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_soft_job.c @@ -0,0 +1,464 @@ +/* + * Copyright (C) 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_soft_job.h" +#include "mali_osk.h" +#include "mali_osk_mali.h" +#include "mali_timeline.h" +#include "mali_session.h" +#include "mali_kernel_common.h" +#include "mali_uk_types.h" +#include "mali_scheduler.h" + +MALI_STATIC_INLINE void mali_soft_job_system_lock(struct mali_soft_job_system *system) +{ + MALI_DEBUG_ASSERT_POINTER(system); + _mali_osk_spinlock_irq_lock(system->lock); + MALI_DEBUG_PRINT(5, ("Mali Soft Job: soft system %p lock taken\n", system)); + MALI_DEBUG_ASSERT(0 == system->lock_owner); + MALI_DEBUG_CODE(system->lock_owner = _mali_osk_get_tid()); +} + +MALI_STATIC_INLINE void mali_soft_job_system_unlock(struct mali_soft_job_system *system) +{ + MALI_DEBUG_ASSERT_POINTER(system); + MALI_DEBUG_PRINT(5, ("Mali Soft Job: releasing soft system %p lock\n", system)); + MALI_DEBUG_ASSERT(_mali_osk_get_tid() == system->lock_owner); + MALI_DEBUG_CODE(system->lock_owner = 0); + _mali_osk_spinlock_irq_unlock(system->lock); +} + +#if defined(DEBUG) +MALI_STATIC_INLINE void mali_soft_job_system_assert_locked(struct mali_soft_job_system *system) +{ + MALI_DEBUG_ASSERT_POINTER(system); + MALI_DEBUG_ASSERT(_mali_osk_get_tid() == system->lock_owner); +} +#define MALI_ASSERT_SOFT_JOB_SYSTEM_LOCKED(system) mali_soft_job_system_assert_locked(system) +#else +#define MALI_ASSERT_SOFT_JOB_SYSTEM_LOCKED(system) +#endif /* defined(DEBUG) */ + +struct mali_soft_job_system *mali_soft_job_system_create(struct mali_session_data *session) +{ + u32 i; + struct mali_soft_job_system *system; + struct mali_soft_job *job; + + MALI_DEBUG_ASSERT_POINTER(session); + + system = (struct mali_soft_job_system *) _mali_osk_calloc(1, sizeof(struct mali_soft_job_system)); + if (NULL == system) { + return NULL; + } + + system->session = session; + + system->lock = _mali_osk_spinlock_irq_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_SCHEDULER); + if (NULL == system->lock) { + mali_soft_job_system_destroy(system); + return NULL; + } + system->lock_owner = 0; + + _MALI_OSK_INIT_LIST_HEAD(&(system->jobs_free)); + _MALI_OSK_INIT_LIST_HEAD(&(system->jobs_used)); + + for (i = 0; i < MALI_MAX_NUM_SOFT_JOBS; ++i) { + job = &(system->jobs[i]); + _mali_osk_list_add(&(job->system_list), &(system->jobs_free)); + job->system = system; + job->state = MALI_SOFT_JOB_STATE_FREE; + job->id = i; + } + + return system; +} + +void mali_soft_job_system_destroy(struct mali_soft_job_system *system) +{ + MALI_DEBUG_ASSERT_POINTER(system); + + /* All jobs should be free at this point. */ + MALI_DEBUG_CODE( { + u32 i; + struct mali_soft_job *job; + + for (i = 0; i < MALI_MAX_NUM_SOFT_JOBS; ++i) + { + job = &(system->jobs[i]); + MALI_DEBUG_ASSERT(MALI_SOFT_JOB_STATE_FREE == job->state); + } + }); + + if (NULL != system) { + if (NULL != system->lock) { + _mali_osk_spinlock_irq_term(system->lock); + } + _mali_osk_free(system); + } +} + +static struct mali_soft_job *mali_soft_job_system_alloc_job(struct mali_soft_job_system *system) +{ + struct mali_soft_job *job; + + MALI_DEBUG_ASSERT_POINTER(system); + MALI_ASSERT_SOFT_JOB_SYSTEM_LOCKED(system); + + if (_mali_osk_list_empty(&(system->jobs_free))) { + /* No jobs available. */ + return NULL; + } + + /* Grab first job and move it to the used list. */ + job = _MALI_OSK_LIST_ENTRY(system->jobs_free.next, struct mali_soft_job, system_list); + MALI_DEBUG_ASSERT(MALI_SOFT_JOB_STATE_FREE == job->state); + + _mali_osk_list_move(&(job->system_list), &(system->jobs_used)); + job->state = MALI_SOFT_JOB_STATE_ALLOCATED; + + MALI_DEBUG_ASSERT(MALI_SOFT_JOB_INVALID_ID != job->id); + MALI_DEBUG_ASSERT(system == job->system); + + return job; +} + +static void mali_soft_job_system_free_job(struct mali_soft_job_system *system, struct mali_soft_job *job) +{ + MALI_DEBUG_ASSERT_POINTER(job); + MALI_DEBUG_ASSERT_POINTER(system); + + mali_soft_job_system_lock(job->system); + + MALI_DEBUG_ASSERT(MALI_SOFT_JOB_STATE_FREE != job->state); + MALI_DEBUG_ASSERT(MALI_SOFT_JOB_INVALID_ID != job->id); + MALI_DEBUG_ASSERT(system == job->system); + + job->state = MALI_SOFT_JOB_STATE_FREE; + _mali_osk_list_move(&(job->system_list), &(system->jobs_free)); + + mali_soft_job_system_unlock(job->system); +} + +MALI_STATIC_INLINE struct mali_soft_job *mali_soft_job_system_lookup_job(struct mali_soft_job_system *system, u32 job_id) +{ + MALI_DEBUG_ASSERT_POINTER(system); + MALI_ASSERT_SOFT_JOB_SYSTEM_LOCKED(system); + + if (job_id < MALI_MAX_NUM_SOFT_JOBS) { + return &system->jobs[job_id]; + } + + return NULL; +} + +void mali_soft_job_destroy(struct mali_soft_job *job) +{ + MALI_DEBUG_ASSERT_POINTER(job); + MALI_DEBUG_ASSERT_POINTER(job->system); + + MALI_DEBUG_PRINT(4, ("Mali Soft Job: destroying soft job %u (0x%08X)\n", job->id, job)); + + if (NULL != job) { + if (0 < _mali_osk_atomic_dec_return(&job->refcount)) return; + + _mali_osk_atomic_term(&job->refcount); + + if (NULL != job->activated_notification) { + _mali_osk_notification_delete(job->activated_notification); + job->activated_notification = NULL; + } + + mali_soft_job_system_free_job(job->system, job); + } +} + +struct mali_soft_job *mali_soft_job_create(struct mali_soft_job_system *system, mali_soft_job_type type, u32 user_job) +{ + struct mali_soft_job *job; + _mali_osk_notification_t *notification = NULL; + + MALI_DEBUG_ASSERT_POINTER(system); + MALI_DEBUG_ASSERT(MALI_SOFT_JOB_TYPE_USER_SIGNALED >= type); + + if (MALI_SOFT_JOB_TYPE_USER_SIGNALED == type) { + notification = _mali_osk_notification_create(_MALI_NOTIFICATION_SOFT_ACTIVATED, sizeof(_mali_uk_soft_job_activated_s)); + if (unlikely(NULL == notification)) { + MALI_PRINT_ERROR(("Mali Soft Job: failed to allocate notification")); + return NULL; + } + } + + mali_soft_job_system_lock(system); + + job = mali_soft_job_system_alloc_job(system); + if (NULL == job) { + mali_soft_job_system_unlock(system); + MALI_PRINT_ERROR(("Mali Soft Job: failed to allocate job")); + _mali_osk_notification_delete(notification); + return NULL; + } + + job->type = type; + job->user_job = user_job; + job->activated = MALI_FALSE; + + if (MALI_SOFT_JOB_TYPE_USER_SIGNALED == type) { + job->activated_notification = notification; + } + + _mali_osk_atomic_init(&job->refcount, 1); + + MALI_DEBUG_ASSERT(MALI_SOFT_JOB_STATE_ALLOCATED == job->state); + MALI_DEBUG_ASSERT(system == job->system); + MALI_DEBUG_ASSERT(MALI_SOFT_JOB_INVALID_ID != job->id); + + mali_soft_job_system_unlock(system); + + return job; +} + +mali_timeline_point mali_soft_job_start(struct mali_soft_job *job, struct mali_timeline_fence *fence) +{ + mali_timeline_point point; + struct mali_soft_job_system *system; + + MALI_DEBUG_ASSERT_POINTER(job); + MALI_DEBUG_ASSERT_POINTER(fence); + + MALI_DEBUG_ASSERT_POINTER(job->system); + system = job->system; + + MALI_DEBUG_ASSERT_POINTER(system->session); + MALI_DEBUG_ASSERT_POINTER(system->session->timeline_system); + + mali_soft_job_system_lock(system); + + MALI_DEBUG_ASSERT(MALI_SOFT_JOB_STATE_ALLOCATED == job->state); + job->state = MALI_SOFT_JOB_STATE_STARTED; + + mali_soft_job_system_unlock(system); + + MALI_DEBUG_PRINT(4, ("Mali Soft Job: starting soft job %u (0x%08X)\n", job->id, job)); + + mali_timeline_tracker_init(&job->tracker, MALI_TIMELINE_TRACKER_SOFT, fence, job); + point = mali_timeline_system_add_tracker(system->session->timeline_system, &job->tracker, MALI_TIMELINE_SOFT); + + return point; +} + +static mali_bool mali_soft_job_is_activated(void *data) +{ + struct mali_soft_job *job; + + job = (struct mali_soft_job *) data; + MALI_DEBUG_ASSERT_POINTER(job); + + return job->activated; +} + +_mali_osk_errcode_t mali_soft_job_system_signal_job(struct mali_soft_job_system *system, u32 job_id) +{ + struct mali_soft_job *job; + struct mali_timeline_system *timeline_system; + mali_scheduler_mask schedule_mask; + + MALI_DEBUG_ASSERT_POINTER(system); + + mali_soft_job_system_lock(system); + + job = mali_soft_job_system_lookup_job(system, job_id); + + if (NULL == job || !(MALI_SOFT_JOB_STATE_STARTED == job->state || MALI_SOFT_JOB_STATE_TIMED_OUT == job->state)) { + mali_soft_job_system_unlock(system); + MALI_PRINT_ERROR(("Mali Soft Job: invalid soft job id %u", job_id)); + return _MALI_OSK_ERR_ITEM_NOT_FOUND; + } + + if (MALI_SOFT_JOB_STATE_TIMED_OUT == job->state) { + job->state = MALI_SOFT_JOB_STATE_SIGNALED; + mali_soft_job_system_unlock(system); + + MALI_DEBUG_ASSERT(MALI_TRUE == job->activated); + MALI_DEBUG_PRINT(4, ("Mali Soft Job: soft job %u (0x%08X) was timed out\n", job->id, job)); + mali_soft_job_destroy(job); + + return _MALI_OSK_ERR_TIMEOUT; + } + + MALI_DEBUG_ASSERT(MALI_SOFT_JOB_STATE_STARTED == job->state); + + job->state = MALI_SOFT_JOB_STATE_SIGNALED; + mali_soft_job_system_unlock(system); + + /* Since the job now is in signaled state, timeouts from the timeline system will be + * ignored, and it is not possible to signal this job again. */ + + timeline_system = system->session->timeline_system; + MALI_DEBUG_ASSERT_POINTER(timeline_system); + + /* Wait until activated. */ + _mali_osk_wait_queue_wait_event(timeline_system->wait_queue, mali_soft_job_is_activated, (void *) job); + + MALI_DEBUG_PRINT(4, ("Mali Soft Job: signaling soft job %u (0x%08X)\n", job->id, job)); + + schedule_mask = mali_timeline_tracker_release(&job->tracker); + mali_scheduler_schedule_from_mask(schedule_mask, MALI_FALSE); + + mali_soft_job_destroy(job); + + return _MALI_OSK_ERR_OK; +} + +static void mali_soft_job_send_activated_notification(struct mali_soft_job *job) +{ + if (NULL != job->activated_notification) { + _mali_uk_soft_job_activated_s *res = job->activated_notification->result_buffer; + res->user_job = job->user_job; + mali_session_send_notification(job->system->session, job->activated_notification); + } + job->activated_notification = NULL; +} + +void mali_soft_job_system_activate_job(struct mali_soft_job *job) +{ + MALI_DEBUG_ASSERT_POINTER(job); + MALI_DEBUG_ASSERT_POINTER(job->system); + MALI_DEBUG_ASSERT_POINTER(job->system->session); + + MALI_DEBUG_PRINT(4, ("Mali Soft Job: Timeline activation for soft job %u (0x%08X).\n", job->id, job)); + + mali_soft_job_system_lock(job->system); + + if (unlikely(job->system->session->is_aborting)) { + MALI_DEBUG_PRINT(3, ("Mali Soft Job: Soft job %u (0x%08X) activated while session is aborting.\n", job->id, job)); + + mali_soft_job_system_unlock(job->system); + + /* Since we are in shutdown, we can ignore the scheduling bitmask. */ + mali_timeline_tracker_release(&job->tracker); + mali_soft_job_destroy(job); + return; + } + + /* Send activated notification. */ + mali_soft_job_send_activated_notification(job); + + /* Wake up sleeping signaler. */ + job->activated = MALI_TRUE; + _mali_osk_wait_queue_wake_up(job->tracker.system->wait_queue); + + mali_soft_job_system_unlock(job->system); +} + +mali_scheduler_mask mali_soft_job_system_timeout_job(struct mali_soft_job *job) +{ + mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY; + + MALI_DEBUG_ASSERT_POINTER(job); + MALI_DEBUG_ASSERT_POINTER(job->system); + MALI_DEBUG_ASSERT_POINTER(job->system->session); + MALI_DEBUG_ASSERT(MALI_TRUE == job->activated); + + MALI_DEBUG_PRINT(4, ("Mali Soft Job: Timeline timeout for soft job %u (0x%08X).\n", job->id, job)); + + mali_soft_job_system_lock(job->system); + + MALI_DEBUG_ASSERT(MALI_SOFT_JOB_STATE_STARTED == job->state || + MALI_SOFT_JOB_STATE_SIGNALED == job->state); + + if (unlikely(job->system->session->is_aborting)) { + /* The session is aborting. This job will be released and destroyed by @ref + * mali_soft_job_system_abort(). */ + mali_soft_job_system_unlock(job->system); + + return MALI_SCHEDULER_MASK_EMPTY; + } + + if (MALI_SOFT_JOB_STATE_STARTED != job->state) { + MALI_DEBUG_ASSERT(MALI_SOFT_JOB_STATE_SIGNALED == job->state); + + /* The job is about to be signaled, ignore timeout. */ + MALI_DEBUG_PRINT(4, ("Mali Soft Job: Timeout on soft job %u (0x%08X) in signaled state.\n", job->id, job)); + mali_soft_job_system_unlock(job->system); + return schedule_mask; + } + + MALI_DEBUG_ASSERT(MALI_SOFT_JOB_STATE_STARTED == job->state); + + job->state = MALI_SOFT_JOB_STATE_TIMED_OUT; + _mali_osk_atomic_inc(&job->refcount); + + mali_soft_job_system_unlock(job->system); + + schedule_mask = mali_timeline_tracker_release(&job->tracker); + + mali_soft_job_destroy(job); + + return schedule_mask; +} + +void mali_soft_job_system_abort(struct mali_soft_job_system *system) +{ + u32 i; + struct mali_soft_job *job, *tmp; + _MALI_OSK_LIST_HEAD_STATIC_INIT(jobs); + + MALI_DEBUG_ASSERT_POINTER(system); + MALI_DEBUG_ASSERT_POINTER(system->session); + MALI_DEBUG_ASSERT(system->session->is_aborting); + + MALI_DEBUG_PRINT(3, ("Mali Soft Job: Aborting soft job system for session 0x%08X.\n", system->session)); + + mali_soft_job_system_lock(system); + + for (i = 0; i < MALI_MAX_NUM_SOFT_JOBS; ++i) { + job = &(system->jobs[i]); + + MALI_DEBUG_ASSERT(MALI_SOFT_JOB_STATE_FREE == job->state || + MALI_SOFT_JOB_STATE_STARTED == job->state || + MALI_SOFT_JOB_STATE_TIMED_OUT == job->state); + + if (MALI_SOFT_JOB_STATE_STARTED == job->state) { + /* If the job has been activated, we have to release the tracker and destroy + * the job. If not, the tracker will be released and the job destroyed when + * it is activated. */ + if (MALI_TRUE == job->activated) { + MALI_DEBUG_PRINT(3, ("Mali Soft Job: Aborting unsignaled soft job %u (0x%08X).\n", job->id, job)); + + job->state = MALI_SOFT_JOB_STATE_SIGNALED; + _mali_osk_list_move(&job->system_list, &jobs); + } + } else if (MALI_SOFT_JOB_STATE_TIMED_OUT == job->state) { + MALI_DEBUG_PRINT(3, ("Mali Soft Job: Aborting timed out soft job %u (0x%08X).\n", job->id, job)); + + /* We need to destroy this soft job. */ + _mali_osk_list_move(&job->system_list, &jobs); + } + } + + mali_soft_job_system_unlock(system); + + /* Release and destroy jobs. */ + _MALI_OSK_LIST_FOREACHENTRY(job, tmp, &jobs, struct mali_soft_job, system_list) { + MALI_DEBUG_ASSERT(MALI_SOFT_JOB_STATE_SIGNALED == job->state || + MALI_SOFT_JOB_STATE_TIMED_OUT == job->state); + + if (MALI_SOFT_JOB_STATE_SIGNALED == job->state) { + mali_timeline_tracker_release(&job->tracker); + } + + /* Move job back to used list before destroying. */ + _mali_osk_list_move(&job->system_list, &system->jobs_used); + + mali_soft_job_destroy(job); + } +} diff --git a/drivers/gpu/arm/mali/common/mali_soft_job.h b/drivers/gpu/arm/mali/common/mali_soft_job.h new file mode 100644 index 00000000000000..af978ab730481d --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_soft_job.h @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_SOFT_JOB_H__ +#define __MALI_SOFT_JOB_H__ + +#include "mali_osk.h" + +#include "mali_timeline.h" + +struct mali_timeline_fence; +struct mali_session_data; +struct mali_soft_job; +struct mali_soft_job_system; + +/** + * Soft job types. + * + * Soft jobs of type MALI_SOFT_JOB_TYPE_USER_SIGNALED will only complete after activation if either + * they are signaled by user-space (@ref mali_soft_job_system_signaled_job) or if they are timed out + * by the Timeline system. + */ +typedef enum mali_soft_job_type { + MALI_SOFT_JOB_TYPE_USER_SIGNALED, +} mali_soft_job_type; + +/** + * Soft job state. + * + * All soft jobs in a soft job system will initially be in state MALI_SOFT_JOB_STATE_FREE. On @ref + * mali_soft_job_system_start_job a job will first be allocated. A job in state + * MALI_SOFT_JOB_STATE_FREE will be picked and the state changed to MALI_SOFT_JOB_STATE_ALLOCATED. + * Once the job is added to the timeline system, the state changes to MALI_SOFT_JOB_STATE_STARTED. + * + * For soft jobs of type MALI_SOFT_JOB_TYPE_USER_SIGNALED the state is changed to + * MALI_SOFT_JOB_STATE_SIGNALED when @ref mali_soft_job_system_signal_job is called and the soft + * job's state is MALI_SOFT_JOB_STATE_STARTED or MALI_SOFT_JOB_STATE_TIMED_OUT. + * + * If a soft job of type MALI_SOFT_JOB_TYPE_USER_SIGNALED is timed out before being signaled, the + * state is changed to MALI_SOFT_JOB_STATE_TIMED_OUT. This can only happen to soft jobs in state + * MALI_SOFT_JOB_STATE_STARTED. + * + * When a soft job's reference count reaches zero, it will be freed and the state returns to + * MALI_SOFT_JOB_STATE_FREE. + */ +typedef enum mali_soft_job_state { + MALI_SOFT_JOB_STATE_FREE, + MALI_SOFT_JOB_STATE_ALLOCATED, + MALI_SOFT_JOB_STATE_STARTED, + MALI_SOFT_JOB_STATE_SIGNALED, + MALI_SOFT_JOB_STATE_TIMED_OUT, +} mali_soft_job_state; + +#define MALI_SOFT_JOB_INVALID_ID ((u32) -1) + +/* Maximum number of soft jobs per soft system. */ +#define MALI_MAX_NUM_SOFT_JOBS 20 + +/** + * Soft job struct. + * + * Soft job can be used to represent any kind of CPU work done in kernel-space. + */ +typedef struct mali_soft_job { + mali_soft_job_type type; /**< Soft job type. Must be one of MALI_SOFT_JOB_TYPE_*. */ + u32 user_job; /**< Identifier for soft job in user space. */ + _mali_osk_atomic_t refcount; /**< Soft jobs are reference counted to prevent premature deletion. */ + struct mali_timeline_tracker tracker; /**< Timeline tracker for soft job. */ + mali_bool activated; /**< MALI_TRUE if the job has been activated, MALI_FALSE if not. */ + _mali_osk_notification_t *activated_notification; /**< Pre-allocated notification object for ACTIVATED_NOTIFICATION. */ + + /* Protected by soft job system lock. */ + u32 id; /**< Used by user-space to find corresponding soft job in kernel-space. */ + mali_soft_job_state state; /**< State of soft job, must be one of MALI_SOFT_JOB_STATE_*. */ + struct mali_soft_job_system *system; /**< The soft job system this job is in. */ + _mali_osk_list_t system_list; /**< List element used by soft job system. */ +} mali_soft_job; + +/** + * Per-session soft job system. + * + * The soft job system is used to manage all soft jobs that belongs to a session. + */ +typedef struct mali_soft_job_system { + struct mali_session_data *session; /**< The session this soft job system belongs to. */ + + struct mali_soft_job jobs[MALI_MAX_NUM_SOFT_JOBS]; /**< Array of all soft jobs in this system. */ + _MALI_OSK_LIST_HEAD(jobs_free); /**< List of all free soft jobs. */ + _MALI_OSK_LIST_HEAD(jobs_used); /**< List of all allocated soft jobs. */ + + _mali_osk_spinlock_irq_t *lock; /**< Lock used to protect soft job system and its soft jobs. */ + u32 lock_owner; /**< Contains tid of thread that locked the system or 0, if not locked. */ +} mali_soft_job_system; + +/** + * Create a soft job system. + * + * @param session The session this soft job system will belong to. + * @return The new soft job system, or NULL if unsuccessful. + */ +struct mali_soft_job_system *mali_soft_job_system_create(struct mali_session_data *session); + +/** + * Destroy a soft job system. + * + * @note The soft job must not have any started or activated jobs. Call @ref + * mali_soft_job_system_abort first. + * + * @param system The soft job system we are destroying. + */ +void mali_soft_job_system_destroy(struct mali_soft_job_system *system); + +/** + * Create a soft job. + * + * @param system Soft job system to create soft job from. + * @param type Type of the soft job. + * @param user_job Identifier for soft job in user space. + * @return New soft job if successful, NULL if not. + */ +struct mali_soft_job *mali_soft_job_create(struct mali_soft_job_system *system, mali_soft_job_type type, u32 user_job); + +/** + * Destroy soft job. + * + * @param job Soft job to destroy. + */ +void mali_soft_job_destroy(struct mali_soft_job *job); + +/** + * Start a soft job. + * + * The soft job will be added to the Timeline system which will then activate it after all + * dependencies have been resolved. + * + * Create soft jobs with @ref mali_soft_job_create before starting them. + * + * @param job Soft job to start. + * @param fence Fence representing dependencies for this soft job. + * @return Point on soft job timeline. + */ +mali_timeline_point mali_soft_job_start(struct mali_soft_job *job, struct mali_timeline_fence *fence); + +/** + * Use by user-space to signal that a soft job has completed. + * + * @note Only valid for soft jobs with type MALI_SOFT_JOB_TYPE_USER_SIGNALED. + * + * @note The soft job must be in state MALI_SOFT_JOB_STATE_STARTED for the signal to be successful. + * + * @note If the soft job was signaled successfully, or it received a time out, the soft job will be + * destroyed after this call and should no longer be used. + * + * @note This function will block until the soft job has been activated. + * + * @param system The soft job system the job was started in. + * @param job_id ID of soft job we are signaling. + * + * @return _MALI_OSK_ERR_ITEM_NOT_FOUND if the soft job ID was invalid, _MALI_OSK_ERR_TIMEOUT if the + * soft job was timed out or _MALI_OSK_ERR_OK if we successfully signaled the soft job. + */ +_mali_osk_errcode_t mali_soft_job_system_signal_job(struct mali_soft_job_system *system, u32 job_id); + +/** + * Used by the Timeline system to activate a soft job. + * + * @param job The soft job that is being activated. + */ +void mali_soft_job_system_activate_job(struct mali_soft_job *job); + +/** + * Used by the Timeline system to timeout a soft job. + * + * A soft job is timed out if it completes or is signaled later than MALI_TIMELINE_TIMEOUT_HZ after + * activation. + * + * @param job The soft job that is being timed out. + * @return A scheduling bitmask. + */ +mali_scheduler_mask mali_soft_job_system_timeout_job(struct mali_soft_job *job); + +/** + * Used to cleanup activated soft jobs in the soft job system on session abort. + * + * @param system The soft job system that is being aborted. + */ +void mali_soft_job_system_abort(struct mali_soft_job_system *system); + +#endif /* __MALI_SOFT_JOB_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_spinlock_reentrant.c b/drivers/gpu/arm/mali/common/mali_spinlock_reentrant.c new file mode 100644 index 00000000000000..bad217f65dde93 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_spinlock_reentrant.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_spinlock_reentrant.h" + +#include "mali_osk.h" +#include "mali_kernel_common.h" + +struct mali_spinlock_reentrant *mali_spinlock_reentrant_init(_mali_osk_lock_order_t lock_order) +{ + struct mali_spinlock_reentrant *spinlock; + + spinlock = _mali_osk_calloc(1, sizeof(struct mali_spinlock_reentrant)); + if (NULL == spinlock) { + return NULL; + } + + spinlock->lock = _mali_osk_spinlock_irq_init(_MALI_OSK_LOCKFLAG_ORDERED, lock_order); + if (NULL == spinlock->lock) { + mali_spinlock_reentrant_term(spinlock); + return NULL; + } + + return spinlock; +} + +void mali_spinlock_reentrant_term(struct mali_spinlock_reentrant *spinlock) +{ + MALI_DEBUG_ASSERT_POINTER(spinlock); + MALI_DEBUG_ASSERT(0 == spinlock->counter && 0 == spinlock->owner); + + if (NULL != spinlock->lock) { + _mali_osk_spinlock_irq_term(spinlock->lock); + } + + _mali_osk_free(spinlock); +} + +void mali_spinlock_reentrant_wait(struct mali_spinlock_reentrant *spinlock, u32 tid) +{ + MALI_DEBUG_ASSERT_POINTER(spinlock); + MALI_DEBUG_ASSERT_POINTER(spinlock->lock); + MALI_DEBUG_ASSERT(0 != tid); + + MALI_DEBUG_PRINT(5, ("%s ^\n", __FUNCTION__)); + + if (tid != spinlock->owner) { + _mali_osk_spinlock_irq_lock(spinlock->lock); + MALI_DEBUG_ASSERT(0 == spinlock->owner && 0 == spinlock->counter); + spinlock->owner = tid; + } + + MALI_DEBUG_PRINT(5, ("%s v\n", __FUNCTION__)); + + ++spinlock->counter; +} + +void mali_spinlock_reentrant_signal(struct mali_spinlock_reentrant *spinlock, u32 tid) +{ + MALI_DEBUG_ASSERT_POINTER(spinlock); + MALI_DEBUG_ASSERT_POINTER(spinlock->lock); + MALI_DEBUG_ASSERT(0 != tid && tid == spinlock->owner); + + --spinlock->counter; + if (0 == spinlock->counter) { + spinlock->owner = 0; + MALI_DEBUG_PRINT(5, ("%s release last\n", __FUNCTION__)); + _mali_osk_spinlock_irq_unlock(spinlock->lock); + } +} diff --git a/drivers/gpu/arm/mali/common/mali_spinlock_reentrant.h b/drivers/gpu/arm/mali/common/mali_spinlock_reentrant.h new file mode 100644 index 00000000000000..16e99b7ff5159a --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_spinlock_reentrant.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_SPINLOCK_REENTRANT_H__ +#define __MALI_SPINLOCK_REENTRANT_H__ + +#include "mali_osk.h" +#include "mali_kernel_common.h" + +/** + * Reentrant spinlock. + */ +struct mali_spinlock_reentrant { + _mali_osk_spinlock_irq_t *lock; + u32 owner; + u32 counter; +}; + +/** + * Create a new reentrant spinlock. + * + * @param lock_order Lock order. + * @return New reentrant spinlock. + */ +struct mali_spinlock_reentrant *mali_spinlock_reentrant_init(_mali_osk_lock_order_t lock_order); + +/** + * Terminate reentrant spinlock and free any associated resources. + * + * @param spinlock Reentrant spinlock to terminate. + */ +void mali_spinlock_reentrant_term(struct mali_spinlock_reentrant *spinlock); + +/** + * Wait for reentrant spinlock to be signaled. + * + * @param spinlock Reentrant spinlock. + * @param tid Thread ID. + */ +void mali_spinlock_reentrant_wait(struct mali_spinlock_reentrant *spinlock, u32 tid); + +/** + * Signal reentrant spinlock. + * + * @param spinlock Reentrant spinlock. + * @param tid Thread ID. + */ +void mali_spinlock_reentrant_signal(struct mali_spinlock_reentrant *spinlock, u32 tid); + +/** + * Check if thread is holding reentrant spinlock. + * + * @param spinlock Reentrant spinlock. + * @param tid Thread ID. + * @return MALI_TRUE if thread is holding spinlock, MALI_FALSE if not. + */ +MALI_STATIC_INLINE mali_bool mali_spinlock_reentrant_is_held(struct mali_spinlock_reentrant *spinlock, u32 tid) +{ + MALI_DEBUG_ASSERT_POINTER(spinlock->lock); + return (tid == spinlock->owner && 0 < spinlock->counter); +} + +#endif /* __MALI_SPINLOCK_REENTRANT_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_timeline.c b/drivers/gpu/arm/mali/common/mali_timeline.c new file mode 100644 index 00000000000000..955c74802ccb33 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_timeline.c @@ -0,0 +1,1374 @@ +/* + * Copyright (C) 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_timeline.h" +#include "mali_kernel_common.h" +#include "mali_osk_mali.h" +#include "mali_scheduler.h" +#include "mali_soft_job.h" +#include "mali_timeline_fence_wait.h" +#include "mali_timeline_sync_fence.h" + +#define MALI_TIMELINE_SYSTEM_LOCKED(system) (mali_spinlock_reentrant_is_held((system)->spinlock, _mali_osk_get_tid())) + +static mali_scheduler_mask mali_timeline_system_release_waiter(struct mali_timeline_system *system, + struct mali_timeline_waiter *waiter); + +#if defined(CONFIG_SYNC) +/* Callback that is called when a sync fence a tracker is waiting on is signaled. */ +static void mali_timeline_sync_fence_callback(struct sync_fence *sync_fence, struct sync_fence_waiter *sync_fence_waiter) +{ + struct mali_timeline_system *system; + struct mali_timeline_waiter *waiter; + struct mali_timeline_tracker *tracker; + mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY; + u32 tid = _mali_osk_get_tid(); + mali_bool is_aborting = MALI_FALSE; + int fence_status = sync_fence->status; + + MALI_DEBUG_ASSERT_POINTER(sync_fence); + MALI_DEBUG_ASSERT_POINTER(sync_fence_waiter); + + tracker = _MALI_OSK_CONTAINER_OF(sync_fence_waiter, struct mali_timeline_tracker, sync_fence_waiter); + MALI_DEBUG_ASSERT_POINTER(tracker); + + system = tracker->system; + MALI_DEBUG_ASSERT_POINTER(system); + MALI_DEBUG_ASSERT_POINTER(system->session); + + mali_spinlock_reentrant_wait(system->spinlock, tid); + + is_aborting = system->session->is_aborting; + if (!is_aborting && (0 > fence_status)) { + MALI_PRINT_ERROR(("Mali Timeline: sync fence fd %d signaled with error %d\n", tracker->fence.sync_fd, fence_status)); + tracker->activation_error |= MALI_TIMELINE_ACTIVATION_ERROR_SYNC_BIT; + } + + waiter = tracker->waiter_sync; + MALI_DEBUG_ASSERT_POINTER(waiter); + + tracker->sync_fence = NULL; + schedule_mask |= mali_timeline_system_release_waiter(system, waiter); + + /* If aborting, wake up sleepers that are waiting for sync fence callbacks to complete. */ + if (is_aborting) { + _mali_osk_wait_queue_wake_up(system->wait_queue); + } + + mali_spinlock_reentrant_signal(system->spinlock, tid); + + sync_fence_put(sync_fence); + + if (!is_aborting) { + mali_scheduler_schedule_from_mask(schedule_mask, MALI_TRUE); + } +} +#endif /* defined(CONFIG_SYNC) */ + +static mali_scheduler_mask mali_timeline_tracker_time_out(struct mali_timeline_tracker *tracker) +{ + MALI_DEBUG_ASSERT_POINTER(tracker); + MALI_DEBUG_ASSERT(MALI_TIMELINE_TRACKER_SOFT == tracker->type); + + return mali_soft_job_system_timeout_job((struct mali_soft_job *) tracker->job); +} + +static void mali_timeline_timer_callback(void *data) +{ + struct mali_timeline_system *system; + struct mali_timeline_tracker *tracker; + struct mali_timeline *timeline; + mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY; + u32 tid = _mali_osk_get_tid(); + + timeline = (struct mali_timeline *) data; + MALI_DEBUG_ASSERT_POINTER(timeline); + + system = timeline->system; + MALI_DEBUG_ASSERT_POINTER(system); + + mali_spinlock_reentrant_wait(system->spinlock, tid); + + if (!system->timer_enabled) { + mali_spinlock_reentrant_signal(system->spinlock, tid); + return; + } + + tracker = timeline->tracker_tail; + timeline->timer_active = MALI_FALSE; + + if (NULL != tracker && MALI_TRUE == tracker->timer_active) { + /* This is likely the delayed work that has been schedule out before cancelled. */ + if (MALI_TIMELINE_TIMEOUT_HZ > (_mali_osk_time_tickcount() - tracker->os_tick_activate)) { + mali_spinlock_reentrant_signal(system->spinlock, tid); + return; + } + + schedule_mask = mali_timeline_tracker_time_out(tracker); + tracker->timer_active = MALI_FALSE; + } else { + MALI_PRINT_ERROR(("Mali Timeline: Soft job timer callback without a waiting tracker.\n")); + } + + mali_spinlock_reentrant_signal(system->spinlock, tid); + + mali_scheduler_schedule_from_mask(schedule_mask, MALI_FALSE); +} + +void mali_timeline_system_stop_timer(struct mali_timeline_system *system) +{ + u32 i; + u32 tid = _mali_osk_get_tid(); + + MALI_DEBUG_ASSERT_POINTER(system); + + mali_spinlock_reentrant_wait(system->spinlock, tid); + system->timer_enabled = MALI_FALSE; + mali_spinlock_reentrant_signal(system->spinlock, tid); + + for (i = 0; i < MALI_TIMELINE_MAX; ++i) { + struct mali_timeline *timeline = system->timelines[i]; + + MALI_DEBUG_ASSERT_POINTER(timeline); + + if (NULL != timeline->delayed_work) { + _mali_osk_wq_delayed_cancel_work_sync(timeline->delayed_work); + timeline->timer_active = MALI_FALSE; + } + } +} + +static void mali_timeline_destroy(struct mali_timeline *timeline) +{ + MALI_DEBUG_ASSERT_POINTER(timeline); + if (NULL != timeline) { + /* Assert that the timeline object has been properly cleaned up before destroying it. */ + MALI_DEBUG_ASSERT(timeline->point_oldest == timeline->point_next); + MALI_DEBUG_ASSERT(NULL == timeline->tracker_head); + MALI_DEBUG_ASSERT(NULL == timeline->tracker_tail); + MALI_DEBUG_ASSERT(NULL == timeline->waiter_head); + MALI_DEBUG_ASSERT(NULL == timeline->waiter_tail); + MALI_DEBUG_ASSERT(NULL != timeline->system); + MALI_DEBUG_ASSERT(MALI_TIMELINE_MAX > timeline->id); + +#if defined(CONFIG_SYNC) + if (NULL != timeline->sync_tl) { + sync_timeline_destroy(timeline->sync_tl); + } +#endif /* defined(CONFIG_SYNC) */ + + if (NULL != timeline->delayed_work) { + _mali_osk_wq_delayed_cancel_work_sync(timeline->delayed_work); + _mali_osk_wq_delayed_delete_work_nonflush(timeline->delayed_work); + } + + _mali_osk_free(timeline); + } +} + +static struct mali_timeline *mali_timeline_create(struct mali_timeline_system *system, enum mali_timeline_id id) +{ + struct mali_timeline *timeline; + + MALI_DEBUG_ASSERT_POINTER(system); + MALI_DEBUG_ASSERT(id < MALI_TIMELINE_MAX); + + timeline = (struct mali_timeline *) _mali_osk_calloc(1, sizeof(struct mali_timeline)); + if (NULL == timeline) { + return NULL; + } + + /* Initially the timeline is empty. */ +#if defined(MALI_TIMELINE_DEBUG_START_POINT) + /* Start the timeline a bit before wrapping when debugging. */ + timeline->point_next = UINT_MAX - MALI_TIMELINE_MAX_POINT_SPAN - 128; +#else + timeline->point_next = 1; +#endif + timeline->point_oldest = timeline->point_next; + + /* The tracker and waiter lists will initially be empty. */ + + timeline->system = system; + timeline->id = id; + + timeline->delayed_work = _mali_osk_wq_delayed_create_work(mali_timeline_timer_callback, timeline); + if (NULL == timeline->delayed_work) { + mali_timeline_destroy(timeline); + return NULL; + } + + timeline->timer_active = MALI_FALSE; + +#if defined(CONFIG_SYNC) + { + char timeline_name[32]; + + switch (id) { + case MALI_TIMELINE_GP: + _mali_osk_snprintf(timeline_name, 32, "mali-%u-gp", _mali_osk_get_pid()); + break; + case MALI_TIMELINE_PP: + _mali_osk_snprintf(timeline_name, 32, "mali-%u-pp", _mali_osk_get_pid()); + break; + case MALI_TIMELINE_SOFT: + _mali_osk_snprintf(timeline_name, 32, "mali-%u-soft", _mali_osk_get_pid()); + break; + default: + MALI_PRINT_ERROR(("Mali Timeline: Invalid timeline id %d\n", id)); + mali_timeline_destroy(timeline); + return NULL; + } + + timeline->sync_tl = mali_sync_timeline_create(timeline_name); + if (NULL == timeline->sync_tl) { + mali_timeline_destroy(timeline); + return NULL; + } + } +#endif /* defined(CONFIG_SYNC) */ + + return timeline; +} + +static void mali_timeline_insert_tracker(struct mali_timeline *timeline, struct mali_timeline_tracker *tracker) +{ + MALI_DEBUG_ASSERT_POINTER(timeline); + MALI_DEBUG_ASSERT_POINTER(tracker); + + if (mali_timeline_is_full(timeline)) { + /* Don't add tracker if timeline is full. */ + tracker->point = MALI_TIMELINE_NO_POINT; + return; + } + + tracker->timeline = timeline; + tracker->point = timeline->point_next; + + /* Find next available point. */ + timeline->point_next++; + if (MALI_TIMELINE_NO_POINT == timeline->point_next) { + timeline->point_next++; + } + + MALI_DEBUG_ASSERT(!mali_timeline_is_empty(timeline)); + + /* Add tracker as new head on timeline's tracker list. */ + if (NULL == timeline->tracker_head) { + /* Tracker list is empty. */ + MALI_DEBUG_ASSERT(NULL == timeline->tracker_tail); + + timeline->tracker_tail = tracker; + + MALI_DEBUG_ASSERT(NULL == tracker->timeline_next); + MALI_DEBUG_ASSERT(NULL == tracker->timeline_prev); + } else { + MALI_DEBUG_ASSERT(NULL == timeline->tracker_head->timeline_next); + + tracker->timeline_prev = timeline->tracker_head; + timeline->tracker_head->timeline_next = tracker; + + MALI_DEBUG_ASSERT(NULL == tracker->timeline_next); + } + timeline->tracker_head = tracker; + + MALI_DEBUG_ASSERT(NULL == timeline->tracker_head->timeline_next); + MALI_DEBUG_ASSERT(NULL == timeline->tracker_tail->timeline_prev); +} + +/* Inserting the waiter object into the given timeline */ +static void mali_timeline_insert_waiter(struct mali_timeline *timeline, struct mali_timeline_waiter *waiter_new) +{ + struct mali_timeline_waiter *waiter_prev; + struct mali_timeline_waiter *waiter_next; + + /* Waiter time must be between timeline head and tail, and there must + * be less than MALI_TIMELINE_MAX_POINT_SPAN elements between */ + MALI_DEBUG_ASSERT(( waiter_new->point - timeline->point_oldest) < MALI_TIMELINE_MAX_POINT_SPAN); + MALI_DEBUG_ASSERT((-waiter_new->point + timeline->point_next) < MALI_TIMELINE_MAX_POINT_SPAN); + + /* Finding out where to put this waiter, in the linked waiter list of the given timeline **/ + waiter_prev = timeline->waiter_head; /* Insert new after waiter_prev */ + waiter_next = NULL; /* Insert new before waiter_next */ + + /* Iterating backwards from head (newest) to tail (oldest) until we + * find the correct spot to insert the new waiter */ + while (waiter_prev && mali_timeline_point_after(waiter_prev->point, waiter_new->point)) { + waiter_next = waiter_prev; + waiter_prev = waiter_prev->timeline_prev; + } + + if (NULL == waiter_prev && NULL == waiter_next) { + /* list is empty */ + timeline->waiter_head = waiter_new; + timeline->waiter_tail = waiter_new; + } else if (NULL == waiter_next) { + /* insert at head */ + waiter_new->timeline_prev = timeline->waiter_head; + timeline->waiter_head->timeline_next = waiter_new; + timeline->waiter_head = waiter_new; + } else if (NULL == waiter_prev) { + /* insert at tail */ + waiter_new->timeline_next = timeline->waiter_tail; + timeline->waiter_tail->timeline_prev = waiter_new; + timeline->waiter_tail = waiter_new; + } else { + /* insert between */ + waiter_new->timeline_next = waiter_next; + waiter_new->timeline_prev = waiter_prev; + waiter_next->timeline_prev = waiter_new; + waiter_prev->timeline_next = waiter_new; + } +} + +static void mali_timeline_update_delayed_work(struct mali_timeline *timeline) +{ + struct mali_timeline_system *system; + struct mali_timeline_tracker *oldest_tracker; + + MALI_DEBUG_ASSERT_POINTER(timeline); + MALI_DEBUG_ASSERT(MALI_TIMELINE_SOFT == timeline->id); + + system = timeline->system; + MALI_DEBUG_ASSERT_POINTER(system); + + MALI_DEBUG_ASSERT(MALI_TIMELINE_SYSTEM_LOCKED(system)); + + /* Timer is disabled, early out. */ + if (!system->timer_enabled) return; + + oldest_tracker = timeline->tracker_tail; + if (NULL != oldest_tracker && 0 == oldest_tracker->trigger_ref_count) { + if (MALI_FALSE == oldest_tracker->timer_active) { + if (MALI_TRUE == timeline->timer_active) { + _mali_osk_wq_delayed_cancel_work_async(timeline->delayed_work); + } + _mali_osk_wq_delayed_schedule_work(timeline->delayed_work, MALI_TIMELINE_TIMEOUT_HZ); + oldest_tracker->timer_active = MALI_TRUE; + timeline->timer_active = MALI_TRUE; + } + } else if (MALI_TRUE == timeline->timer_active) { + _mali_osk_wq_delayed_cancel_work_async(timeline->delayed_work); + timeline->timer_active = MALI_FALSE; + } +} + +static mali_scheduler_mask mali_timeline_update_oldest_point(struct mali_timeline *timeline) +{ + mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY; + + MALI_DEBUG_ASSERT_POINTER(timeline); + + MALI_DEBUG_CODE({ + struct mali_timeline_system *system = timeline->system; + MALI_DEBUG_ASSERT_POINTER(system); + + MALI_DEBUG_ASSERT(MALI_TIMELINE_SYSTEM_LOCKED(system)); + }); + + if (NULL != timeline->tracker_tail) { + /* Set oldest point to oldest tracker's point */ + timeline->point_oldest = timeline->tracker_tail->point; + } else { + /* No trackers, mark point list as empty */ + timeline->point_oldest = timeline->point_next; + } + + /* Release all waiters no longer on the timeline's point list. + * Releasing a waiter can trigger this function to be called again, so + * we do not store any pointers on stack. */ + while (NULL != timeline->waiter_tail) { + u32 waiter_time_relative; + u32 time_head_relative; + struct mali_timeline_waiter *waiter = timeline->waiter_tail; + + time_head_relative = timeline->point_next - timeline->point_oldest; + waiter_time_relative = waiter->point - timeline->point_oldest; + + if (waiter_time_relative < time_head_relative) { + /* This and all following waiters are on the point list, so we are done. */ + break; + } + + /* Remove waiter from timeline's waiter list. */ + if (NULL != waiter->timeline_next) { + waiter->timeline_next->timeline_prev = NULL; + } else { + /* This was the last waiter */ + timeline->waiter_head = NULL; + } + timeline->waiter_tail = waiter->timeline_next; + + /* Release waiter. This could activate a tracker, if this was + * the last waiter for the tracker. */ + schedule_mask |= mali_timeline_system_release_waiter(timeline->system, waiter); + } + + return schedule_mask; +} + +void mali_timeline_tracker_init(struct mali_timeline_tracker *tracker, + mali_timeline_tracker_type type, + struct mali_timeline_fence *fence, + void *job) +{ + MALI_DEBUG_ASSERT_POINTER(tracker); + MALI_DEBUG_ASSERT_POINTER(job); + + MALI_DEBUG_ASSERT(MALI_TIMELINE_TRACKER_MAX > type); + + /* Zero out all tracker members. */ + _mali_osk_memset(tracker, 0, sizeof(*tracker)); + + tracker->type = type; + tracker->job = job; + tracker->trigger_ref_count = 1; /* Prevents any callback from trigging while adding it */ + tracker->os_tick_create = _mali_osk_time_tickcount(); + MALI_DEBUG_CODE(tracker->magic = MALI_TIMELINE_TRACKER_MAGIC); + + tracker->activation_error = MALI_TIMELINE_ACTIVATION_ERROR_NONE; + + /* Copy fence. */ + if (NULL != fence) { + _mali_osk_memcpy(&tracker->fence, fence, sizeof(struct mali_timeline_fence)); + } +} + +mali_scheduler_mask mali_timeline_tracker_release(struct mali_timeline_tracker *tracker) +{ + struct mali_timeline *timeline; + struct mali_timeline_system *system; + struct mali_timeline_tracker *tracker_next, *tracker_prev; + mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY; + u32 tid = _mali_osk_get_tid(); + + /* Upon entry a group lock will be held, but not a scheduler lock. */ + MALI_DEBUG_ASSERT_POINTER(tracker); + MALI_DEBUG_ASSERT(MALI_TIMELINE_TRACKER_MAGIC == tracker->magic); + + /* Tracker should have been triggered */ + MALI_DEBUG_ASSERT(0 == tracker->trigger_ref_count); + + /* All waiters should have been released at this point */ + MALI_DEBUG_ASSERT(NULL == tracker->waiter_head); + MALI_DEBUG_ASSERT(NULL == tracker->waiter_tail); + + MALI_DEBUG_PRINT(3, ("Mali Timeline: releasing tracker for job 0x%08X\n", tracker->job)); + + timeline = tracker->timeline; + if (NULL == timeline) { + /* Tracker was not on a timeline, there is nothing to release. */ + return MALI_SCHEDULER_MASK_EMPTY; + } + + system = timeline->system; + MALI_DEBUG_ASSERT_POINTER(system); + + mali_spinlock_reentrant_wait(system->spinlock, tid); + + /* Tracker should still be on timeline */ + MALI_DEBUG_ASSERT(!mali_timeline_is_empty(timeline)); + MALI_DEBUG_ASSERT( mali_timeline_is_point_on(timeline, tracker->point)); + + /* Tracker is no longer valid. */ + MALI_DEBUG_CODE(tracker->magic = 0); + + tracker_next = tracker->timeline_next; + tracker_prev = tracker->timeline_prev; + tracker->timeline_next = NULL; + tracker->timeline_prev = NULL; + + /* Removing tracker from timeline's tracker list */ + if (NULL == tracker_next) { + /* This tracker was the head */ + timeline->tracker_head = tracker_prev; + } else { + tracker_next->timeline_prev = tracker_prev; + } + + if (NULL == tracker_prev) { + /* This tracker was the tail */ + timeline->tracker_tail = tracker_next; + MALI_DEBUG_ASSERT(MALI_TIMELINE_SYSTEM_LOCKED(system)); + /* Update the timeline's oldest time and release any waiters */ + schedule_mask |= mali_timeline_update_oldest_point(timeline); + MALI_DEBUG_ASSERT(MALI_TIMELINE_SYSTEM_LOCKED(system)); + } else { + tracker_prev->timeline_next = tracker_next; + } + + MALI_DEBUG_ASSERT(MALI_TIMELINE_SYSTEM_LOCKED(system)); + + /* Update delayed work only when it is the soft job timeline */ + if (MALI_TIMELINE_SOFT == tracker->timeline->id) { + mali_timeline_update_delayed_work(tracker->timeline); + } + + mali_spinlock_reentrant_signal(system->spinlock, tid); + + return schedule_mask; +} + +void mali_timeline_system_release_waiter_list(struct mali_timeline_system *system, + struct mali_timeline_waiter *tail, + struct mali_timeline_waiter *head) +{ + MALI_DEBUG_ASSERT_POINTER(system); + MALI_DEBUG_ASSERT_POINTER(head); + MALI_DEBUG_ASSERT_POINTER(tail); + MALI_DEBUG_ASSERT(MALI_TIMELINE_SYSTEM_LOCKED(system)); + + head->tracker_next = system->waiter_empty_list; + system->waiter_empty_list = tail; +} + +static mali_scheduler_mask mali_timeline_tracker_activate(struct mali_timeline_tracker *tracker) +{ + mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY; + struct mali_timeline_system *system; + struct mali_timeline *timeline; + u32 tid = _mali_osk_get_tid(); + + MALI_DEBUG_ASSERT_POINTER(tracker); + MALI_DEBUG_ASSERT(MALI_TIMELINE_TRACKER_MAGIC == tracker->magic); + + system = tracker->system; + MALI_DEBUG_ASSERT_POINTER(system); + MALI_DEBUG_ASSERT(MALI_TIMELINE_SYSTEM_LOCKED(system)); + + tracker->os_tick_activate = _mali_osk_time_tickcount(); + + if (NULL != tracker->waiter_head) { + mali_timeline_system_release_waiter_list(system, tracker->waiter_tail, tracker->waiter_head); + tracker->waiter_head = NULL; + tracker->waiter_tail = NULL; + } + + switch (tracker->type) { + case MALI_TIMELINE_TRACKER_GP: + schedule_mask = mali_gp_scheduler_activate_job((struct mali_gp_job *) tracker->job); + break; + case MALI_TIMELINE_TRACKER_PP: + schedule_mask = mali_pp_scheduler_activate_job((struct mali_pp_job *) tracker->job); + break; + case MALI_TIMELINE_TRACKER_SOFT: + timeline = tracker->timeline; + MALI_DEBUG_ASSERT_POINTER(timeline); + + mali_soft_job_system_activate_job((struct mali_soft_job *) tracker->job); + + /* Start a soft timer to make sure the soft job be released in a limited time */ + mali_spinlock_reentrant_wait(system->spinlock, tid); + mali_timeline_update_delayed_work(timeline); + mali_spinlock_reentrant_signal(system->spinlock, tid); + break; + case MALI_TIMELINE_TRACKER_WAIT: + mali_timeline_fence_wait_activate((struct mali_timeline_fence_wait_tracker *) tracker->job); + break; + case MALI_TIMELINE_TRACKER_SYNC: +#if defined(CONFIG_SYNC) + mali_timeline_sync_fence_activate((struct mali_timeline_sync_fence_tracker *) tracker->job); +#else + MALI_PRINT_ERROR(("Mali Timeline: sync tracker not supported\n", tracker->type)); +#endif /* defined(CONFIG_SYNC) */ + break; + default: + MALI_PRINT_ERROR(("Mali Timeline - Illegal tracker type: %d\n", tracker->type)); + break; + } + + return schedule_mask; +} + +void mali_timeline_system_tracker_get(struct mali_timeline_system *system, struct mali_timeline_tracker *tracker) +{ + u32 tid = _mali_osk_get_tid(); + + MALI_DEBUG_ASSERT_POINTER(tracker); + MALI_DEBUG_ASSERT_POINTER(system); + + mali_spinlock_reentrant_wait(system->spinlock, tid); + + MALI_DEBUG_ASSERT(0 < tracker->trigger_ref_count); + tracker->trigger_ref_count++; + + mali_spinlock_reentrant_signal(system->spinlock, tid); +} + +mali_scheduler_mask mali_timeline_system_tracker_put(struct mali_timeline_system *system, struct mali_timeline_tracker *tracker, mali_timeline_activation_error activation_error) +{ + u32 tid = _mali_osk_get_tid(); + mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY; + + MALI_DEBUG_ASSERT_POINTER(tracker); + MALI_DEBUG_ASSERT_POINTER(system); + + mali_spinlock_reentrant_wait(system->spinlock, tid); + + MALI_DEBUG_ASSERT(0 < tracker->trigger_ref_count); + tracker->trigger_ref_count--; + + tracker->activation_error |= activation_error; + + if (0 == tracker->trigger_ref_count) { + schedule_mask |= mali_timeline_tracker_activate(tracker); + tracker = NULL; + } + + mali_spinlock_reentrant_signal(system->spinlock, tid); + + return schedule_mask; +} + +void mali_timeline_fence_copy_uk_fence(struct mali_timeline_fence *fence, _mali_uk_fence_t *uk_fence) +{ + u32 i; + + MALI_DEBUG_ASSERT_POINTER(fence); + MALI_DEBUG_ASSERT_POINTER(uk_fence); + + for (i = 0; i < MALI_TIMELINE_MAX; ++i) { + fence->points[i] = uk_fence->points[i]; + } + + fence->sync_fd = uk_fence->sync_fd; +} + +struct mali_timeline_system *mali_timeline_system_create(struct mali_session_data *session) +{ + u32 i; + struct mali_timeline_system *system; + + MALI_DEBUG_ASSERT_POINTER(session); + MALI_DEBUG_PRINT(4, ("Mali Timeline: creating timeline system\n")); + + system = (struct mali_timeline_system *) _mali_osk_calloc(1, sizeof(struct mali_timeline_system)); + if (NULL == system) { + return NULL; + } + + system->spinlock = mali_spinlock_reentrant_init(_MALI_OSK_LOCK_ORDER_TIMELINE_SYSTEM); + if (NULL == system->spinlock) { + mali_timeline_system_destroy(system); + return NULL; + } + + for (i = 0; i < MALI_TIMELINE_MAX; ++i) { + system->timelines[i] = mali_timeline_create(system, (enum mali_timeline_id)i); + if (NULL == system->timelines[i]) { + mali_timeline_system_destroy(system); + return NULL; + } + } + +#if defined(CONFIG_SYNC) + system->signaled_sync_tl = mali_sync_timeline_create("mali-always-signaled"); + if (NULL == system->signaled_sync_tl) { + mali_timeline_system_destroy(system); + return NULL; + } +#endif /* defined(CONFIG_SYNC) */ + + system->waiter_empty_list = NULL; + system->session = session; + system->timer_enabled = MALI_TRUE; + + system->wait_queue = _mali_osk_wait_queue_init(); + if (NULL == system->wait_queue) { + mali_timeline_system_destroy(system); + return NULL; + } + + return system; +} + +#if defined(CONFIG_SYNC) + +/** + * Check if there are any trackers left on timeline. + * + * Used as a wait queue conditional. + * + * @param data Timeline. + * @return MALI_TRUE if there are no trackers on timeline, MALI_FALSE if not. + */ +static mali_bool mali_timeline_has_no_trackers(void *data) +{ + struct mali_timeline *timeline = (struct mali_timeline *) data; + + MALI_DEBUG_ASSERT_POINTER(timeline); + + return mali_timeline_is_empty(timeline); +} + +/** + * Cancel sync fence waiters waited upon by trackers on all timelines. + * + * Will return after all timelines have no trackers left. + * + * @param system Timeline system. + */ +static void mali_timeline_cancel_sync_fence_waiters(struct mali_timeline_system *system) +{ + u32 i; + u32 tid = _mali_osk_get_tid(); + struct mali_timeline_tracker *tracker, *tracker_next; + _MALI_OSK_LIST_HEAD_STATIC_INIT(tracker_list); + + MALI_DEBUG_ASSERT_POINTER(system); + MALI_DEBUG_ASSERT_POINTER(system->session); + MALI_DEBUG_ASSERT(system->session->is_aborting); + + mali_spinlock_reentrant_wait(system->spinlock, tid); + + /* Cancel sync fence waiters. */ + for (i = 0; i < MALI_TIMELINE_MAX; ++i) { + struct mali_timeline *timeline = system->timelines[i]; + + MALI_DEBUG_ASSERT_POINTER(timeline); + + tracker_next = timeline->tracker_tail; + while (NULL != tracker_next) { + tracker = tracker_next; + tracker_next = tracker->timeline_next; + + if (NULL == tracker->sync_fence) continue; + + MALI_DEBUG_PRINT(3, ("Mali Timeline: Cancelling sync fence wait for tracker 0x%08X.\n", tracker)); + + /* Cancel sync fence waiter. */ + if (0 == sync_fence_cancel_async(tracker->sync_fence, &tracker->sync_fence_waiter)) { + /* Callback was not called, move tracker to local list. */ + _mali_osk_list_add(&tracker->sync_fence_cancel_list, &tracker_list); + } + } + } + + mali_spinlock_reentrant_signal(system->spinlock, tid); + + /* Manually call sync fence callback in order to release waiter and trigger activation of tracker. */ + _MALI_OSK_LIST_FOREACHENTRY(tracker, tracker_next, &tracker_list, struct mali_timeline_tracker, sync_fence_cancel_list) { + mali_timeline_sync_fence_callback(tracker->sync_fence, &tracker->sync_fence_waiter); + } + + /* Sleep until all sync fence callbacks are done and all timelines are empty. */ + for (i = 0; i < MALI_TIMELINE_MAX; ++i) { + struct mali_timeline *timeline = system->timelines[i]; + + MALI_DEBUG_ASSERT_POINTER(timeline); + + _mali_osk_wait_queue_wait_event(system->wait_queue, mali_timeline_has_no_trackers, (void *) timeline); + } +} + +#endif /* defined(CONFIG_SYNC) */ + +void mali_timeline_system_abort(struct mali_timeline_system *system) +{ + MALI_DEBUG_CODE(u32 tid = _mali_osk_get_tid();); + + MALI_DEBUG_ASSERT_POINTER(system); + MALI_DEBUG_ASSERT_POINTER(system->session); + MALI_DEBUG_ASSERT(system->session->is_aborting); + + MALI_DEBUG_PRINT(3, ("Mali Timeline: Aborting timeline system for session 0x%08X.\n", system->session)); + +#if defined(CONFIG_SYNC) + mali_timeline_cancel_sync_fence_waiters(system); +#endif /* defined(CONFIG_SYNC) */ + + /* Should not be any waiters or trackers left at this point. */ + MALI_DEBUG_CODE( { + u32 i; + mali_spinlock_reentrant_wait(system->spinlock, tid); + for (i = 0; i < MALI_TIMELINE_MAX; ++i) + { + struct mali_timeline *timeline = system->timelines[i]; + MALI_DEBUG_ASSERT_POINTER(timeline); + MALI_DEBUG_ASSERT(timeline->point_oldest == timeline->point_next); + MALI_DEBUG_ASSERT(NULL == timeline->tracker_head); + MALI_DEBUG_ASSERT(NULL == timeline->tracker_tail); + MALI_DEBUG_ASSERT(NULL == timeline->waiter_head); + MALI_DEBUG_ASSERT(NULL == timeline->waiter_tail); + } + mali_spinlock_reentrant_signal(system->spinlock, tid); + }); +} + +void mali_timeline_system_destroy(struct mali_timeline_system *system) +{ + u32 i; + struct mali_timeline_waiter *waiter, *next; + + MALI_DEBUG_ASSERT_POINTER(system); + MALI_DEBUG_ASSERT_POINTER(system->session); + + MALI_DEBUG_PRINT(4, ("Mali Timeline: destroying timeline system\n")); + + if (NULL != system) { + /* There should be no waiters left on this queue. */ + if (NULL != system->wait_queue) { + _mali_osk_wait_queue_term(system->wait_queue); + system->wait_queue = NULL; + } + + /* Free all waiters in empty list */ + waiter = system->waiter_empty_list; + while (NULL != waiter) { + next = waiter->tracker_next; + _mali_osk_free(waiter); + waiter = next; + } + +#if defined(CONFIG_SYNC) + if (NULL != system->signaled_sync_tl) { + sync_timeline_destroy(system->signaled_sync_tl); + } +#endif /* defined(CONFIG_SYNC) */ + + for (i = 0; i < MALI_TIMELINE_MAX; ++i) { + if (NULL != system->timelines[i]) { + mali_timeline_destroy(system->timelines[i]); + } + } + if (NULL != system->spinlock) { + mali_spinlock_reentrant_term(system->spinlock); + } + + _mali_osk_free(system); + } +} + +/** + * Find how many waiters are needed for a given fence. + * + * @param fence The fence to check. + * @return Number of waiters needed for fence. + */ +static u32 mali_timeline_fence_num_waiters(struct mali_timeline_fence *fence) +{ + u32 i, num_waiters = 0; + + MALI_DEBUG_ASSERT_POINTER(fence); + + for (i = 0; i < MALI_TIMELINE_MAX; ++i) { + if (MALI_TIMELINE_NO_POINT != fence->points[i]) { + ++num_waiters; + } + } + +#if defined(CONFIG_SYNC) + if (-1 != fence->sync_fd) ++num_waiters; +#endif /* defined(CONFIG_SYNC) */ + + return num_waiters; +} + +static struct mali_timeline_waiter *mali_timeline_system_get_zeroed_waiter(struct mali_timeline_system *system) +{ + struct mali_timeline_waiter *waiter; + + MALI_DEBUG_ASSERT_POINTER(system); + MALI_DEBUG_ASSERT(MALI_TIMELINE_SYSTEM_LOCKED(system)); + + waiter = system->waiter_empty_list; + if (NULL != waiter) { + /* Remove waiter from empty list and zero it */ + system->waiter_empty_list = waiter->tracker_next; + _mali_osk_memset(waiter, 0, sizeof(*waiter)); + } + + /* Return NULL if list was empty. */ + return waiter; +} + +static void mali_timeline_system_allocate_waiters(struct mali_timeline_system *system, + struct mali_timeline_waiter **tail, + struct mali_timeline_waiter **head, + int max_num_waiters) +{ + u32 i, tid = _mali_osk_get_tid(); + mali_bool do_alloc; + struct mali_timeline_waiter *waiter; + + MALI_DEBUG_ASSERT_POINTER(system); + MALI_DEBUG_ASSERT_POINTER(tail); + MALI_DEBUG_ASSERT_POINTER(head); + + MALI_DEBUG_ASSERT(MALI_TIMELINE_SYSTEM_LOCKED(system)); + + *head = *tail = NULL; + do_alloc = MALI_FALSE; + i = 0; + while (i < max_num_waiters) { + if (MALI_FALSE == do_alloc) { + waiter = mali_timeline_system_get_zeroed_waiter(system); + if (NULL == waiter) { + do_alloc = MALI_TRUE; + mali_spinlock_reentrant_signal(system->spinlock, tid); + continue; + } + } else { + waiter = _mali_osk_calloc(1, sizeof(struct mali_timeline_waiter)); + if (NULL == waiter) break; + } + ++i; + if (NULL == *tail) { + *tail = waiter; + *head = waiter; + } else { + (*head)->tracker_next = waiter; + *head = waiter; + } + } + if (MALI_TRUE == do_alloc) { + mali_spinlock_reentrant_wait(system->spinlock, tid); + } +} + +/** + * Create waiters for the given tracker. The tracker is activated when all waiters are release. + * + * @note Tracker can potentially be activated before this function returns. + * + * @param system Timeline system. + * @param tracker Tracker we will create waiters for. + * @param waiter_tail List of pre-allocated waiters. + * @param waiter_head List of pre-allocated waiters. + */ +static void mali_timeline_system_create_waiters_and_unlock(struct mali_timeline_system *system, + struct mali_timeline_tracker *tracker, + struct mali_timeline_waiter *waiter_tail, + struct mali_timeline_waiter *waiter_head) +{ + int i; + u32 tid = _mali_osk_get_tid(); + mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY; +#if defined(CONFIG_SYNC) + struct sync_fence *sync_fence = NULL; +#endif /* defined(CONFIG_SYNC) */ + + MALI_DEBUG_ASSERT_POINTER(system); + MALI_DEBUG_ASSERT_POINTER(tracker); + + MALI_DEBUG_ASSERT(MALI_TIMELINE_SYSTEM_LOCKED(system)); + + MALI_DEBUG_ASSERT(NULL == tracker->waiter_head); + MALI_DEBUG_ASSERT(NULL == tracker->waiter_tail); + MALI_DEBUG_ASSERT(NULL != tracker->job); + + /* Creating waiter object for all the timelines the fence is put on. Inserting this waiter + * into the timelines sorted list of waiters */ + for (i = 0; i < MALI_TIMELINE_MAX; ++i) { + mali_timeline_point point; + struct mali_timeline *timeline; + struct mali_timeline_waiter *waiter; + + /* Get point on current timeline from tracker's fence. */ + point = tracker->fence.points[i]; + + if (likely(MALI_TIMELINE_NO_POINT == point)) { + /* Fence contains no point on this timeline so we don't need a waiter. */ + continue; + } + + timeline = system->timelines[i]; + MALI_DEBUG_ASSERT_POINTER(timeline); + + if (unlikely(!mali_timeline_is_point_valid(timeline, point))) { + MALI_PRINT_ERROR(("Mali Timeline: point %d is not valid (oldest=%d, next=%d)\n", + point, timeline->point_oldest, timeline->point_next)); + continue; + } + + if (likely(mali_timeline_is_point_released(timeline, point))) { + /* Tracker representing the point has been released so we don't need a + * waiter. */ + continue; + } + + /* The point is on timeline. */ + MALI_DEBUG_ASSERT(mali_timeline_is_point_on(timeline, point)); + + /* Get a new zeroed waiter object. */ + if (likely(NULL != waiter_tail)) { + waiter = waiter_tail; + waiter_tail = waiter_tail->tracker_next; + } else { + MALI_PRINT_ERROR(("Mali Timeline: failed to allocate memory for waiter\n")); + continue; + } + + /* Yanking the trigger ref count of the tracker. */ + tracker->trigger_ref_count++; + + waiter->point = point; + waiter->tracker = tracker; + + /* Insert waiter on tracker's singly-linked waiter list. */ + if (NULL == tracker->waiter_head) { + /* list is empty */ + MALI_DEBUG_ASSERT(NULL == tracker->waiter_tail); + tracker->waiter_tail = waiter; + } else { + tracker->waiter_head->tracker_next = waiter; + } + tracker->waiter_head = waiter; + + /* Add waiter to timeline. */ + mali_timeline_insert_waiter(timeline, waiter); + } +#if defined(CONFIG_SYNC) + if (-1 != tracker->fence.sync_fd) { + int ret; + struct mali_timeline_waiter *waiter; + + sync_fence = sync_fence_fdget(tracker->fence.sync_fd); + if (unlikely(NULL == sync_fence)) { + MALI_PRINT_ERROR(("Mali Timeline: failed to get sync fence from fd %d\n", tracker->fence.sync_fd)); + goto exit; + } + + /* Check if we have a zeroed waiter object available. */ + if (unlikely(NULL == waiter_tail)) { + MALI_PRINT_ERROR(("Mali Timeline: failed to allocate memory for waiter\n")); + goto exit; + } + + /* Start asynchronous wait that will release waiter when the fence is signaled. */ + sync_fence_waiter_init(&tracker->sync_fence_waiter, mali_timeline_sync_fence_callback); + ret = sync_fence_wait_async(sync_fence, &tracker->sync_fence_waiter); + if (1 == ret) { + /* Fence already signaled, no waiter needed. */ + goto exit; + } else if (0 != ret) { + MALI_PRINT_ERROR(("Mali Timeline: sync fence fd %d signaled with error %d\n", tracker->fence.sync_fd, ret)); + tracker->activation_error |= MALI_TIMELINE_ACTIVATION_ERROR_SYNC_BIT; + goto exit; + } + + /* Grab new zeroed waiter object. */ + waiter = waiter_tail; + waiter_tail = waiter_tail->tracker_next; + + /* Increase the trigger ref count of the tracker. */ + tracker->trigger_ref_count++; + + waiter->point = MALI_TIMELINE_NO_POINT; + waiter->tracker = tracker; + + /* Insert waiter on tracker's singly-linked waiter list. */ + if (NULL == tracker->waiter_head) { + /* list is empty */ + MALI_DEBUG_ASSERT(NULL == tracker->waiter_tail); + tracker->waiter_tail = waiter; + } else { + tracker->waiter_head->tracker_next = waiter; + } + tracker->waiter_head = waiter; + + /* Also store waiter in separate field for easy access by sync callback. */ + tracker->waiter_sync = waiter; + + /* Store the sync fence in tracker so we can retrieve in abort session, if needed. */ + tracker->sync_fence = sync_fence; + + sync_fence = NULL; + } +exit: +#endif /* defined(CONFIG_SYNC) */ + + if (NULL != waiter_tail) { + mali_timeline_system_release_waiter_list(system, waiter_tail, waiter_head); + } + + /* Release the initial trigger ref count. */ + tracker->trigger_ref_count--; + + /* If there were no waiters added to this tracker we activate immediately. */ + if (0 == tracker->trigger_ref_count) { + schedule_mask |= mali_timeline_tracker_activate(tracker); + } + + mali_spinlock_reentrant_signal(system->spinlock, tid); + +#if defined(CONFIG_SYNC) + if (NULL != sync_fence) { + sync_fence_put(sync_fence); + } +#endif /* defined(CONFIG_SYNC) */ + + mali_scheduler_schedule_from_mask(schedule_mask, MALI_FALSE); +} + +mali_timeline_point mali_timeline_system_add_tracker(struct mali_timeline_system *system, + struct mali_timeline_tracker *tracker, + enum mali_timeline_id timeline_id) +{ + int num_waiters = 0; + struct mali_timeline_waiter *waiter_tail, *waiter_head; + u32 tid = _mali_osk_get_tid(); + mali_timeline_point point = MALI_TIMELINE_NO_POINT; + + MALI_DEBUG_ASSERT_POINTER(system); + MALI_DEBUG_ASSERT_POINTER(system->session); + MALI_DEBUG_ASSERT_POINTER(tracker); + + MALI_DEBUG_ASSERT(MALI_FALSE == system->session->is_aborting); + MALI_DEBUG_ASSERT(MALI_TIMELINE_TRACKER_MAX > tracker->type); + MALI_DEBUG_ASSERT(MALI_TIMELINE_TRACKER_MAGIC == tracker->magic); + + MALI_DEBUG_PRINT(4, ("Mali Timeline: adding tracker for job %p, timeline: %d\n", tracker->job, timeline_id)); + + MALI_DEBUG_ASSERT(0 < tracker->trigger_ref_count); + tracker->system = system; + + mali_spinlock_reentrant_wait(system->spinlock, tid); + + num_waiters = mali_timeline_fence_num_waiters(&tracker->fence); + + /* Allocate waiters. */ + mali_timeline_system_allocate_waiters(system, &waiter_tail, &waiter_head, num_waiters); + MALI_DEBUG_ASSERT(MALI_TIMELINE_SYSTEM_LOCKED(system)); + + /* Add tracker to timeline. This will allocate a point for the tracker on the timeline. If + * timeline ID is MALI_TIMELINE_NONE the tracker will NOT be added to a timeline and the + * point will be MALI_TIMELINE_NO_POINT. + * + * NOTE: the tracker can fail to be added if the timeline is full. If this happens, the + * point will be MALI_TIMELINE_NO_POINT. */ + MALI_DEBUG_ASSERT(timeline_id < MALI_TIMELINE_MAX || timeline_id == MALI_TIMELINE_NONE); + if (likely(timeline_id < MALI_TIMELINE_MAX)) { + struct mali_timeline *timeline = system->timelines[timeline_id]; + mali_timeline_insert_tracker(timeline, tracker); + MALI_DEBUG_ASSERT(!mali_timeline_is_empty(timeline)); + } + + point = tracker->point; + + /* Create waiters for tracker based on supplied fence. Each waiter will increase the + * trigger ref count. */ + mali_timeline_system_create_waiters_and_unlock(system, tracker, waiter_tail, waiter_head); + tracker = NULL; + + /* At this point the tracker object might have been freed so we should no longer + * access it. */ + + + /* The tracker will always be activated after calling add_tracker, even if NO_POINT is + * returned. */ + return point; +} + +static mali_scheduler_mask mali_timeline_system_release_waiter(struct mali_timeline_system *system, + struct mali_timeline_waiter *waiter) +{ + struct mali_timeline_tracker *tracker; + mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY; + + MALI_DEBUG_ASSERT_POINTER(system); + MALI_DEBUG_ASSERT_POINTER(waiter); + + MALI_DEBUG_ASSERT(MALI_TIMELINE_SYSTEM_LOCKED(system)); + + tracker = waiter->tracker; + MALI_DEBUG_ASSERT_POINTER(tracker); + + /* At this point the waiter has been removed from the timeline's waiter list, but it is + * still on the tracker's waiter list. All of the tracker's waiters will be released when + * the tracker is activated. */ + + waiter->point = MALI_TIMELINE_NO_POINT; + waiter->tracker = NULL; + + tracker->trigger_ref_count--; + if (0 == tracker->trigger_ref_count) { + /* This was the last waiter; activate tracker */ + schedule_mask |= mali_timeline_tracker_activate(tracker); + tracker = NULL; + } + + return schedule_mask; +} + +mali_timeline_point mali_timeline_system_get_latest_point(struct mali_timeline_system *system, + enum mali_timeline_id timeline_id) +{ + mali_timeline_point point; + struct mali_timeline *timeline; + u32 tid = _mali_osk_get_tid(); + + MALI_DEBUG_ASSERT_POINTER(system); + + if (MALI_TIMELINE_MAX <= timeline_id) { + return MALI_TIMELINE_NO_POINT; + } + + mali_spinlock_reentrant_wait(system->spinlock, tid); + + timeline = system->timelines[timeline_id]; + MALI_DEBUG_ASSERT_POINTER(timeline); + + point = MALI_TIMELINE_NO_POINT; + if (timeline->point_oldest != timeline->point_next) { + point = timeline->point_next - 1; + if (MALI_TIMELINE_NO_POINT == point) point--; + } + + mali_spinlock_reentrant_signal(system->spinlock, tid); + + return point; +} + +#if defined(MALI_TIMELINE_DEBUG_FUNCTIONS) + +static mali_bool is_waiting_on_timeline(struct mali_timeline_tracker *tracker, enum mali_timeline_id id) +{ + struct mali_timeline *timeline; + struct mali_timeline_system *system; + + MALI_DEBUG_ASSERT_POINTER(tracker); + + MALI_DEBUG_ASSERT_POINTER(tracker->timeline); + timeline = tracker->timeline; + + MALI_DEBUG_ASSERT_POINTER(timeline->system); + system = timeline->system; + + if (MALI_TIMELINE_MAX > id) { + return mali_timeline_is_point_on(system->timelines[id], tracker->fence.points[id]); + } else { + MALI_DEBUG_ASSERT(MALI_TIMELINE_NONE == id); + return MALI_FALSE; + } +} + +static const char *timeline_id_to_string(enum mali_timeline_id id) +{ + switch (id) { + case MALI_TIMELINE_GP: + return " GP"; + case MALI_TIMELINE_PP: + return " PP"; + case MALI_TIMELINE_SOFT: + return "SOFT"; + default: + return "NONE"; + } +} + +static const char *timeline_tracker_type_to_string(enum mali_timeline_tracker_type type) +{ + switch (type) { + case MALI_TIMELINE_TRACKER_GP: + return " GP"; + case MALI_TIMELINE_TRACKER_PP: + return " PP"; + case MALI_TIMELINE_TRACKER_SOFT: + return "SOFT"; + case MALI_TIMELINE_TRACKER_WAIT: + return "WAIT"; + case MALI_TIMELINE_TRACKER_SYNC: + return "SYNC"; + default: + return "INVALID"; + } +} + +mali_timeline_tracker_state mali_timeline_debug_get_tracker_state(struct mali_timeline_tracker *tracker) +{ + struct mali_timeline *timeline = NULL; + + MALI_DEBUG_ASSERT_POINTER(tracker); + timeline = tracker->timeline; + + if (0 != tracker->trigger_ref_count) { + return MALI_TIMELINE_TS_WAITING; + } + + if (timeline && (timeline->tracker_tail == tracker || NULL != tracker->timeline_prev)) { + return MALI_TIMELINE_TS_ACTIVE; + } + + if (timeline && (MALI_TIMELINE_NO_POINT == tracker->point)) { + return MALI_TIMELINE_TS_INIT; + } + + return MALI_TIMELINE_TS_FINISH; +} + +void mali_timeline_debug_print_tracker(struct mali_timeline_tracker *tracker) +{ + const char *tracker_state = "IWAF"; + + MALI_DEBUG_ASSERT_POINTER(tracker); + + if (0 != tracker->trigger_ref_count) { + MALI_PRINTF(("TL: %s %u %c - ref_wait:%u [%s%u,%s%u,%s%u,%d] (0x%08X)\n", + timeline_tracker_type_to_string(tracker->type), tracker->point, + *(tracker_state + mali_timeline_debug_get_tracker_state(tracker)), + tracker->trigger_ref_count, + is_waiting_on_timeline(tracker, MALI_TIMELINE_GP) ? "W" : " ", tracker->fence.points[0], + is_waiting_on_timeline(tracker, MALI_TIMELINE_PP) ? "W" : " ", tracker->fence.points[1], + is_waiting_on_timeline(tracker, MALI_TIMELINE_SOFT) ? "W" : " ", tracker->fence.points[2], + tracker->fence.sync_fd, tracker->job)); + } else { + MALI_PRINTF(("TL: %s %u %c (0x%08X)\n", + timeline_tracker_type_to_string(tracker->type), tracker->point, + *(tracker_state + mali_timeline_debug_get_tracker_state(tracker)), + tracker->job)); + } +} + +void mali_timeline_debug_print_timeline(struct mali_timeline *timeline) +{ + struct mali_timeline_tracker *tracker = NULL; + int i_max = 30; + + MALI_DEBUG_ASSERT_POINTER(timeline); + + tracker = timeline->tracker_tail; + while (NULL != tracker && 0 < --i_max) { + mali_timeline_debug_print_tracker(tracker); + tracker = tracker->timeline_next; + } + + if (0 == i_max) { + MALI_PRINTF(("TL: Too many trackers in list to print\n")); + } +} + +void mali_timeline_debug_print_system(struct mali_timeline_system *system) +{ + int i; + int num_printed = 0; + + MALI_DEBUG_ASSERT_POINTER(system); + + /* Print all timelines */ + for (i = 0; i < MALI_TIMELINE_MAX; ++i) { + struct mali_timeline *timeline = system->timelines[i]; + + MALI_DEBUG_ASSERT_POINTER(timeline); + + if (NULL == timeline->tracker_head) continue; + + MALI_PRINTF(("TL: Timeline %s:\n", + timeline_id_to_string((enum mali_timeline_id)i))); + mali_timeline_debug_print_timeline(timeline); + num_printed++; + } + + if (0 == num_printed) { + MALI_PRINTF(("TL: All timelines empty\n")); + } +} + +#endif /* defined(MALI_TIMELINE_DEBUG_FUNCTIONS) */ diff --git a/drivers/gpu/arm/mali/common/mali_timeline.h b/drivers/gpu/arm/mali/common/mali_timeline.h new file mode 100644 index 00000000000000..e014637bf7d4c8 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_timeline.h @@ -0,0 +1,494 @@ +/* + * Copyright (C) 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_TIMELINE_H__ +#define __MALI_TIMELINE_H__ + +#include "mali_osk.h" +#include "mali_ukk.h" +#include "mali_session.h" +#include "mali_kernel_common.h" +#include "mali_spinlock_reentrant.h" +#include "mali_sync.h" +#include "mali_scheduler_types.h" + +/** + * Soft job timeout. + * + * Soft jobs have to be signaled as complete after activation. Normally this is done by user space, + * but in order to guarantee that every soft job is completed, we also have a timer. + */ +#define MALI_TIMELINE_TIMEOUT_HZ ((u32) (HZ * 3 / 2)) /* 1500 ms. */ + +/** + * Timeline type. + */ +typedef enum mali_timeline_id { + MALI_TIMELINE_GP = MALI_UK_TIMELINE_GP, /**< GP job timeline. */ + MALI_TIMELINE_PP = MALI_UK_TIMELINE_PP, /**< PP job timeline. */ + MALI_TIMELINE_SOFT = MALI_UK_TIMELINE_SOFT, /**< Soft job timeline. */ + MALI_TIMELINE_MAX = MALI_UK_TIMELINE_MAX +} mali_timeline_id; + +/** + * Used by trackers that should not be added to a timeline (@ref mali_timeline_system_add_tracker). + */ +#define MALI_TIMELINE_NONE MALI_TIMELINE_MAX + +/** + * Tracker type. + */ +typedef enum mali_timeline_tracker_type { + MALI_TIMELINE_TRACKER_GP = 0, /**< Tracker used by GP jobs. */ + MALI_TIMELINE_TRACKER_PP = 1, /**< Tracker used by PP jobs. */ + MALI_TIMELINE_TRACKER_SOFT = 2, /**< Tracker used by soft jobs. */ + MALI_TIMELINE_TRACKER_WAIT = 3, /**< Tracker used for fence wait. */ + MALI_TIMELINE_TRACKER_SYNC = 4, /**< Tracker used for sync fence. */ + MALI_TIMELINE_TRACKER_MAX = 5, +} mali_timeline_tracker_type; + +/** + * Tracker activation error. + */ +typedef u32 mali_timeline_activation_error; +#define MALI_TIMELINE_ACTIVATION_ERROR_NONE 0 +#define MALI_TIMELINE_ACTIVATION_ERROR_SYNC_BIT (1<<1) +#define MALI_TIMELINE_ACTIVATION_ERROR_FATAL_BIT (1<<0) + +/** + * Type used to represent a point on a timeline. + */ +typedef u32 mali_timeline_point; + +/** + * Used to represent that no point on a timeline. + */ +#define MALI_TIMELINE_NO_POINT ((mali_timeline_point) 0) + +/** + * The maximum span of points on a timeline. A timeline will be considered full if the difference + * between the oldest and newest points is equal or larger to this value. + */ +#define MALI_TIMELINE_MAX_POINT_SPAN 65536 + +/** + * Magic value used to assert on validity of trackers. + */ +#define MALI_TIMELINE_TRACKER_MAGIC 0xabcdabcd + +struct mali_timeline; +struct mali_timeline_waiter; +struct mali_timeline_tracker; + +/** + * Timeline fence. + */ +struct mali_timeline_fence { + mali_timeline_point points[MALI_TIMELINE_MAX]; /**< For each timeline, a point or MALI_TIMELINE_NO_POINT. */ + s32 sync_fd; /**< A file descriptor representing a sync fence, or -1. */ +}; + +/** + * Timeline system. + * + * The Timeline system has a set of timelines associated with a session. + */ +struct mali_timeline_system { + struct mali_spinlock_reentrant *spinlock; /**< Spin lock protecting the timeline system */ + struct mali_timeline *timelines[MALI_TIMELINE_MAX]; /**< The timelines in this system */ + + /* Single-linked list of unused waiter objects. Uses the tracker_next field in tracker. */ + struct mali_timeline_waiter *waiter_empty_list; + + struct mali_session_data *session; /**< Session that owns this system. */ + + mali_bool timer_enabled; /**< Set to MALI_TRUE if soft job timer should be enabled, MALI_FALSE if not. */ + + _mali_osk_wait_queue_t *wait_queue; /**< Wait queue. */ + +#if defined(CONFIG_SYNC) + struct sync_timeline *signaled_sync_tl; /**< Special sync timeline used to create pre-signaled sync fences */ +#endif /* defined(CONFIG_SYNC) */ +}; + +/** + * Timeline. Each Timeline system will have MALI_TIMELINE_MAX timelines. + */ +struct mali_timeline { + mali_timeline_point point_next; /**< The next available point. */ + mali_timeline_point point_oldest; /**< The oldest point not released. */ + + /* Double-linked list of trackers. Sorted in ascending order by tracker->time_number with + * tail pointing to the tracker with the oldest time. */ + struct mali_timeline_tracker *tracker_head; + struct mali_timeline_tracker *tracker_tail; + + /* Double-linked list of waiters. Sorted in ascending order by waiter->time_number_wait + * with tail pointing to the waiter with oldest wait time. */ + struct mali_timeline_waiter *waiter_head; + struct mali_timeline_waiter *waiter_tail; + + struct mali_timeline_system *system; /**< Timeline system this timeline belongs to. */ + enum mali_timeline_id id; /**< Timeline type. */ + +#if defined(CONFIG_SYNC) + struct sync_timeline *sync_tl; /**< Sync timeline that corresponds to this timeline. */ +#endif /* defined(CONFIG_SYNC) */ + + /* The following fields are used to time out soft job trackers. */ + _mali_osk_wq_delayed_work_t *delayed_work; + mali_bool timer_active; +}; + +/** + * Timeline waiter. + */ +struct mali_timeline_waiter { + mali_timeline_point point; /**< Point on timeline we are waiting for to be released. */ + struct mali_timeline_tracker *tracker; /**< Tracker that is waiting. */ + + struct mali_timeline_waiter *timeline_next; /**< Next waiter on timeline's waiter list. */ + struct mali_timeline_waiter *timeline_prev; /**< Previous waiter on timeline's waiter list. */ + + struct mali_timeline_waiter *tracker_next; /**< Next waiter on tracker's waiter list. */ +}; + +/** + * Timeline tracker. + */ +struct mali_timeline_tracker { + MALI_DEBUG_CODE(u32 magic); /**< Should always be MALI_TIMELINE_TRACKER_MAGIC for a valid tracker. */ + + mali_timeline_point point; /**< Point on timeline for this tracker */ + + struct mali_timeline_tracker *timeline_next; /**< Next tracker on timeline's tracker list */ + struct mali_timeline_tracker *timeline_prev; /**< Previous tracker on timeline's tracker list */ + + u32 trigger_ref_count; /**< When zero tracker will be activated */ + mali_timeline_activation_error activation_error; /**< Activation error. */ + struct mali_timeline_fence fence; /**< Fence used to create this tracker */ + + /* Single-linked list of waiters. Sorted in order of insertions with + * tail pointing to first waiter. */ + struct mali_timeline_waiter *waiter_head; + struct mali_timeline_waiter *waiter_tail; + +#if defined(CONFIG_SYNC) + /* These are only used if the tracker is waiting on a sync fence. */ + struct mali_timeline_waiter *waiter_sync; /**< A direct pointer to timeline waiter representing sync fence. */ + struct sync_fence_waiter sync_fence_waiter; /**< Used to connect sync fence and tracker in sync fence wait callback. */ + struct sync_fence *sync_fence; /**< The sync fence this tracker is waiting on. */ + _mali_osk_list_t sync_fence_cancel_list; /**< List node used to cancel sync fence waiters. */ +#endif /* defined(CONFIG_SYNC) */ + + struct mali_timeline_system *system; /**< Timeline system. */ + struct mali_timeline *timeline; /**< Timeline, or NULL if not on a timeline. */ + enum mali_timeline_tracker_type type; /**< Type of tracker. */ + void *job; /**< Owner of tracker. */ + + /* The following fields are used to time out soft job trackers. */ + u32 os_tick_create; + u32 os_tick_activate; + mali_bool timer_active; +}; + +/** + * What follows is a set of functions to check the state of a timeline and to determine where on a + * timeline a given point is. Most of these checks will translate the timeline so the oldest point + * on the timeline is aligned with zero. Remember that all of these calculation are done on + * unsigned integers. + * + * The following example illustrates the three different states a point can be in. The timeline has + * been translated to put the oldest point at zero: + * + * + * + * [ point is in forbidden zone ] + * 64k wide + * MALI_TIMELINE_MAX_POINT_SPAN + * + * [ point is on timeline ) ( point is released ] + * + * 0--------------------------##############################--------------------2^32 - 1 + * ^ ^ + * \ | + * oldest point on timeline | + * \ + * next point on timeline + */ + +/** + * Compare two timeline points + * + * Returns true if a is after b, false if a is before or equal to b. + * + * This funcion ignores MALI_TIMELINE_MAX_POINT_SPAN. Wrapping is supported and + * the result will be correct if the points is less then UINT_MAX/2 apart. + * + * @param a Point on timeline + * @param b Point on timeline + * @return MALI_TRUE if a is after b + */ +MALI_STATIC_INLINE mali_bool mali_timeline_point_after(mali_timeline_point a, mali_timeline_point b) +{ + return 0 > ((s32)b) - ((s32)a); +} + +/** + * Check if a point is on timeline. A point is on a timeline if it is greater than, or equal to, + * the oldest point, and less than the next point. + * + * @param timeline Timeline. + * @param point Point on timeline. + * @return MALI_TRUE if point is on timeline, MALI_FALSE if not. + */ +MALI_STATIC_INLINE mali_bool mali_timeline_is_point_on(struct mali_timeline *timeline, mali_timeline_point point) +{ + MALI_DEBUG_ASSERT_POINTER(timeline); + MALI_DEBUG_ASSERT(MALI_TIMELINE_NO_POINT != point); + + return (point - timeline->point_oldest) < (timeline->point_next - timeline->point_oldest); +} + +/** + * Check if a point has been released. A point is released if it is older than the oldest point on + * the timeline, newer than the next point, and also not in the forbidden zone. + * + * @param timeline Timeline. + * @param point Point on timeline. + * @return MALI_TRUE if point has been release, MALI_FALSE if not. + */ +MALI_STATIC_INLINE mali_bool mali_timeline_is_point_released(struct mali_timeline *timeline, mali_timeline_point point) +{ + mali_timeline_point point_normalized; + mali_timeline_point next_normalized; + + MALI_DEBUG_ASSERT_POINTER(timeline); + MALI_DEBUG_ASSERT(MALI_TIMELINE_NO_POINT != point); + + point_normalized = point - timeline->point_oldest; + next_normalized = timeline->point_next - timeline->point_oldest; + + return point_normalized > (next_normalized + MALI_TIMELINE_MAX_POINT_SPAN); +} + +/** + * Check if a point is valid. A point is valid if is on the timeline or has been released. + * + * @param timeline Timeline. + * @param point Point on timeline. + * @return MALI_TRUE if point is valid, MALI_FALSE if not. + */ +MALI_STATIC_INLINE mali_bool mali_timeline_is_point_valid(struct mali_timeline *timeline, mali_timeline_point point) +{ + MALI_DEBUG_ASSERT_POINTER(timeline); + return mali_timeline_is_point_on(timeline, point) || mali_timeline_is_point_released(timeline, point); +} + +/** + * Check if timeline is empty (has no points on it). A timeline is empty if next == oldest. + * + * @param timeline Timeline. + * @return MALI_TRUE if timeline is empty, MALI_FALSE if not. + */ +MALI_STATIC_INLINE mali_bool mali_timeline_is_empty(struct mali_timeline *timeline) +{ + MALI_DEBUG_ASSERT_POINTER(timeline); + return timeline->point_next == timeline->point_oldest; +} + +/** + * Check if timeline is full. A valid timeline cannot span more than 64k points (@ref + * MALI_TIMELINE_MAX_POINT_SPAN). + * + * @param timeline Timeline. + * @return MALI_TRUE if timeline is full, MALI_FALSE if not. + */ +MALI_STATIC_INLINE mali_bool mali_timeline_is_full(struct mali_timeline *timeline) +{ + MALI_DEBUG_ASSERT_POINTER(timeline); + return MALI_TIMELINE_MAX_POINT_SPAN <= (timeline->point_next - timeline->point_oldest); +} + +/** + * Create a new timeline system. + * + * @param session The session this timeline system will belong to. + * @return New timeline system. + */ +struct mali_timeline_system *mali_timeline_system_create(struct mali_session_data *session); + +/** + * Abort timeline system. + * + * This will release all pending waiters in the timeline system causing all trackers to be + * activated. + * + * @param system Timeline system to abort all jobs from. + */ +void mali_timeline_system_abort(struct mali_timeline_system *system); + +/** + * Destroy an empty timeline system. + * + * @note @ref mali_timeline_system_abort() should be called prior to this function. + * + * @param system Timeline system to destroy. + */ +void mali_timeline_system_destroy(struct mali_timeline_system *system); + +/** + * Stop the soft job timer. + * + * @param system Timeline system + */ +void mali_timeline_system_stop_timer(struct mali_timeline_system *system); + +/** + * Add a tracker to a timeline system and optionally also on a timeline. + * + * Once added to the timeline system, the tracker is guaranteed to be activated. The tracker can be + * activated before this function returns. Thus, it is also possible that the tracker is released + * before this function returns, depending on the tracker type. + * + * @note Tracker must be initialized (@ref mali_timeline_tracker_init) before being added to the + * timeline system. + * + * @param system Timeline system the tracker will be added to. + * @param tracker The tracker to be added. + * @param timeline_id Id of the timeline the tracker will be added to, or + * MALI_TIMELINE_NONE if it should not be added on a timeline. + * @return Point on timeline identifying this tracker, or MALI_TIMELINE_NO_POINT if not on timeline. + */ +mali_timeline_point mali_timeline_system_add_tracker(struct mali_timeline_system *system, + struct mali_timeline_tracker *tracker, + enum mali_timeline_id timeline_id); + +/** + * Get latest point on timeline. + * + * @param system Timeline system. + * @param timeline_id Id of timeline to get latest point from. + * @return Latest point on timeline, or MALI_TIMELINE_NO_POINT if the timeline is empty. + */ +mali_timeline_point mali_timeline_system_get_latest_point(struct mali_timeline_system *system, + enum mali_timeline_id timeline_id); + +/** + * Initialize tracker. + * + * Must be called before tracker is added to timeline system (@ref mali_timeline_system_add_tracker). + * + * @param tracker Tracker to initialize. + * @param type Type of tracker. + * @param fence Fence used to set up dependencies for tracker. + * @param job Pointer to job struct this tracker is associated with. + */ +void mali_timeline_tracker_init(struct mali_timeline_tracker *tracker, + mali_timeline_tracker_type type, + struct mali_timeline_fence *fence, + void *job); + +/** + * Grab trigger ref count on tracker. + * + * This will prevent tracker from being activated until the trigger ref count reaches zero. + * + * @note Tracker must have been initialized (@ref mali_timeline_tracker_init). + * + * @param system Timeline system. + * @param tracker Tracker. + */ +void mali_timeline_system_tracker_get(struct mali_timeline_system *system, struct mali_timeline_tracker *tracker); + +/** + * Release trigger ref count on tracker. + * + * If the trigger ref count reaches zero, the tracker will be activated. + * + * @param system Timeline system. + * @param tracker Tracker. + * @param activation_error Error bitmask if activated with error, or MALI_TIMELINE_ACTIVATION_ERROR_NONE if no error. + * @return Scheduling bitmask. + */ +mali_scheduler_mask mali_timeline_system_tracker_put(struct mali_timeline_system *system, struct mali_timeline_tracker *tracker, mali_timeline_activation_error activation_error); + +/** + * Release a tracker from the timeline system. + * + * This is used to signal that the job being tracker is finished, either due to normal circumstances + * (job complete/abort) or due to a timeout. + * + * We may need to schedule some subsystems after a tracker has been released and the returned + * bitmask will tell us if it is necessary. If the return value is non-zero, this value needs to be + * sent as an input parameter to @ref mali_scheduler_schedule_from_mask() to do the scheduling. + * + * @note Tracker must have been activated before being released. + * @warning Not calling @ref mali_scheduler_schedule_from_mask() after releasing a tracker can lead + * to a deadlock. + * + * @param tracker Tracker being released. + * @return Scheduling bitmask. + */ +mali_scheduler_mask mali_timeline_tracker_release(struct mali_timeline_tracker *tracker); + +/** + * Copy data from a UK fence to a Timeline fence. + * + * @param fence Timeline fence. + * @param uk_fence UK fence. + */ +void mali_timeline_fence_copy_uk_fence(struct mali_timeline_fence *fence, _mali_uk_fence_t *uk_fence); + +#define MALI_TIMELINE_DEBUG_FUNCTIONS +#if defined(MALI_TIMELINE_DEBUG_FUNCTIONS) + +/** + * Tracker state. Used for debug printing. + */ +typedef enum mali_timeline_tracker_state { + MALI_TIMELINE_TS_INIT = 0, + MALI_TIMELINE_TS_WAITING = 1, + MALI_TIMELINE_TS_ACTIVE = 2, + MALI_TIMELINE_TS_FINISH = 3, +} mali_timeline_tracker_state; + +/** + * Get tracker state. + * + * @param tracker Tracker to check. + * @return State of tracker. + */ +mali_timeline_tracker_state mali_timeline_debug_get_tracker_state(struct mali_timeline_tracker *tracker); + +/** + * Print debug information about tracker. + * + * @param tracker Tracker to print. + */ +void mali_timeline_debug_print_tracker(struct mali_timeline_tracker *tracker); + +/** + * Print debug information about timeline. + * + * @param timeline Timeline to print. + */ +void mali_timeline_debug_print_timeline(struct mali_timeline *timeline); + +/** + * Print debug information about timeline system. + * + * @param system Timeline system to print. + */ +void mali_timeline_debug_print_system(struct mali_timeline_system *system); + +#endif /* defined(MALI_TIMELINE_DEBUG_FUNCTIONS) */ + +#endif /* __MALI_TIMELINE_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_timeline_fence_wait.c b/drivers/gpu/arm/mali/common/mali_timeline_fence_wait.c new file mode 100644 index 00000000000000..752bec7a21cf37 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_timeline_fence_wait.c @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_timeline_fence_wait.h" + +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_spinlock_reentrant.h" + +/** + * Allocate a fence waiter tracker. + * + * @return New fence waiter if successful, NULL if not. + */ +static struct mali_timeline_fence_wait_tracker *mali_timeline_fence_wait_tracker_alloc(void) +{ + return (struct mali_timeline_fence_wait_tracker *) _mali_osk_calloc(1, sizeof(struct mali_timeline_fence_wait_tracker)); +} + +/** + * Free fence waiter tracker. + * + * @param wait Fence wait tracker to free. + */ +static void mali_timeline_fence_wait_tracker_free(struct mali_timeline_fence_wait_tracker *wait) +{ + MALI_DEBUG_ASSERT_POINTER(wait); + _mali_osk_atomic_term(&wait->refcount); + _mali_osk_free(wait); +} + +/** + * Check if fence wait tracker has been activated. Used as a wait queue condition. + * + * @param data Fence waiter. + * @return MALI_TRUE if tracker has been activated, MALI_FALSE if not. + */ +static mali_bool mali_timeline_fence_wait_tracker_is_activated(void *data) +{ + struct mali_timeline_fence_wait_tracker *wait; + + wait = (struct mali_timeline_fence_wait_tracker *) data; + MALI_DEBUG_ASSERT_POINTER(wait); + + return wait->activated; +} + +/** + * Check if fence has been signaled. + * + * @param system Timeline system. + * @param fence Timeline fence. + * @return MALI_TRUE if fence is signaled, MALI_FALSE if not. + */ +static mali_bool mali_timeline_fence_wait_check_status(struct mali_timeline_system *system, struct mali_timeline_fence *fence) +{ + int i; + u32 tid = _mali_osk_get_tid(); + mali_bool ret = MALI_TRUE; +#if defined(CONFIG_SYNC) + struct sync_fence *sync_fence = NULL; +#endif + + MALI_DEBUG_ASSERT_POINTER(system); + MALI_DEBUG_ASSERT_POINTER(fence); + + mali_spinlock_reentrant_wait(system->spinlock, tid); + + for (i = 0; i < MALI_TIMELINE_MAX; ++i) { + struct mali_timeline *timeline; + mali_timeline_point point; + + point = fence->points[i]; + + if (likely(MALI_TIMELINE_NO_POINT == point)) { + /* Fence contains no point on this timeline. */ + continue; + } + + timeline = system->timelines[i]; + MALI_DEBUG_ASSERT_POINTER(timeline); + + if (unlikely(!mali_timeline_is_point_valid(timeline, point))) { + MALI_PRINT_ERROR(("Mali Timeline: point %d is not valid (oldest=%d, next=%d)\n", point, timeline->point_oldest, timeline->point_next)); + } + + if (!mali_timeline_is_point_released(timeline, point)) { + ret = MALI_FALSE; + goto exit; + } + } + +#if defined(CONFIG_SYNC) + if (-1 != fence->sync_fd) { + sync_fence = sync_fence_fdget(fence->sync_fd); + if (likely(NULL != sync_fence)) { + if (0 == sync_fence->status) { + ret = MALI_FALSE; + } + } else { + MALI_PRINT_ERROR(("Mali Timeline: failed to get sync fence from fd %d\n", fence->sync_fd)); + } + } +#endif /* defined(CONFIG_SYNC) */ + +exit: + mali_spinlock_reentrant_signal(system->spinlock, tid); + +#if defined(CONFIG_SYNC) + if (NULL != sync_fence) { + sync_fence_put(sync_fence); + } +#endif /* defined(CONFIG_SYNC) */ + + return ret; +} + +mali_bool mali_timeline_fence_wait(struct mali_timeline_system *system, struct mali_timeline_fence *fence, u32 timeout) +{ + struct mali_timeline_fence_wait_tracker *wait; + mali_timeline_point point; + mali_bool ret; + + MALI_DEBUG_ASSERT_POINTER(system); + MALI_DEBUG_ASSERT_POINTER(fence); + + MALI_DEBUG_PRINT(4, ("Mali Timeline: wait on fence\n")); + + if (MALI_TIMELINE_FENCE_WAIT_TIMEOUT_IMMEDIATELY == timeout) { + return mali_timeline_fence_wait_check_status(system, fence); + } + + wait = mali_timeline_fence_wait_tracker_alloc(); + if (unlikely(NULL == wait)) { + MALI_PRINT_ERROR(("Mali Timeline: failed to allocate data for fence wait\n")); + return MALI_FALSE; + } + + wait->activated = MALI_FALSE; + wait->system = system; + + /* Initialize refcount to two references. The reference first will be released by this + * function after the wait is over. The second reference will be released when the tracker + * is activated. */ + _mali_osk_atomic_init(&wait->refcount, 2); + + /* Add tracker to timeline system, but not to a timeline. */ + mali_timeline_tracker_init(&wait->tracker, MALI_TIMELINE_TRACKER_WAIT, fence, wait); + point = mali_timeline_system_add_tracker(system, &wait->tracker, MALI_TIMELINE_NONE); + MALI_DEBUG_ASSERT(MALI_TIMELINE_NO_POINT == point); + MALI_IGNORE(point); + + /* Wait for the tracker to be activated or time out. */ + if (MALI_TIMELINE_FENCE_WAIT_TIMEOUT_NEVER == timeout) { + _mali_osk_wait_queue_wait_event(system->wait_queue, mali_timeline_fence_wait_tracker_is_activated, (void *) wait); + } else { + _mali_osk_wait_queue_wait_event_timeout(system->wait_queue, mali_timeline_fence_wait_tracker_is_activated, (void *) wait, timeout); + } + + ret = wait->activated; + + if (0 == _mali_osk_atomic_dec_return(&wait->refcount)) { + mali_timeline_fence_wait_tracker_free(wait); + } + + return ret; +} + +void mali_timeline_fence_wait_activate(struct mali_timeline_fence_wait_tracker *wait) +{ + mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY; + + MALI_DEBUG_ASSERT_POINTER(wait); + MALI_DEBUG_ASSERT_POINTER(wait->system); + + MALI_DEBUG_PRINT(4, ("Mali Timeline: activation for fence wait tracker\n")); + + MALI_DEBUG_ASSERT(MALI_FALSE == wait->activated); + wait->activated = MALI_TRUE; + + _mali_osk_wait_queue_wake_up(wait->system->wait_queue); + + /* Nothing can wait on this tracker, so nothing to schedule after release. */ + schedule_mask = mali_timeline_tracker_release(&wait->tracker); + MALI_DEBUG_ASSERT(MALI_SCHEDULER_MASK_EMPTY == schedule_mask); + MALI_IGNORE(schedule_mask); + + if (0 == _mali_osk_atomic_dec_return(&wait->refcount)) { + mali_timeline_fence_wait_tracker_free(wait); + } +} diff --git a/drivers/gpu/arm/mali/common/mali_timeline_fence_wait.h b/drivers/gpu/arm/mali/common/mali_timeline_fence_wait.h new file mode 100644 index 00000000000000..b73e60899ce122 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_timeline_fence_wait.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_timeline_fence_wait.h + * + * This file contains functions used to wait until a Timeline fence is signaled. + */ + +#ifndef __MALI_TIMELINE_FENCE_WAIT_H__ +#define __MALI_TIMELINE_FENCE_WAIT_H__ + +#include "mali_osk.h" +#include "mali_timeline.h" + +/** + * If used as the timeout argument in @ref mali_timeline_fence_wait, a timer is not used and the + * function only returns when the fence is signaled. + */ +#define MALI_TIMELINE_FENCE_WAIT_TIMEOUT_NEVER ((u32) -1) + +/** + * If used as the timeout argument in @ref mali_timeline_fence_wait, the function will return + * immediately with the current state of the fence. + */ +#define MALI_TIMELINE_FENCE_WAIT_TIMEOUT_IMMEDIATELY 0 + +/** + * Fence wait tracker. + * + * The fence wait tracker is added to the Timeline system with the fence we are waiting on as a + * dependency. We will then perform a blocking wait, possibly with a timeout, until the tracker is + * activated, which happens when the fence is signaled. + */ +struct mali_timeline_fence_wait_tracker { + mali_bool activated; /**< MALI_TRUE if the tracker has been activated, MALI_FALSE if not. */ + _mali_osk_atomic_t refcount; /**< Reference count. */ + struct mali_timeline_system *system; /**< Timeline system. */ + struct mali_timeline_tracker tracker; /**< Timeline tracker. */ +}; + +/** + * Wait for a fence to be signaled, or timeout is reached. + * + * @param system Timeline system. + * @param fence Fence to wait on. + * @param timeout Timeout in ms, or MALI_TIMELINE_FENCE_WAIT_TIMEOUT_NEVER or + * MALI_TIMELINE_FENCE_WAIT_TIMEOUT_IMMEDIATELY. + * @return MALI_TRUE if signaled, MALI_FALSE if timed out. + */ +mali_bool mali_timeline_fence_wait(struct mali_timeline_system *system, struct mali_timeline_fence *fence, u32 timeout); + +/** + * Used by the Timeline system to activate a fence wait tracker. + * + * @param fence_wait_tracker Fence waiter tracker. + */ +void mali_timeline_fence_wait_activate(struct mali_timeline_fence_wait_tracker *fence_wait_tracker); + +#endif /* __MALI_TIMELINE_FENCE_WAIT_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_timeline_sync_fence.c b/drivers/gpu/arm/mali/common/mali_timeline_sync_fence.c new file mode 100644 index 00000000000000..c1ad291ee2e8fd --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_timeline_sync_fence.c @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_timeline_sync_fence.h" + +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_sync.h" + +#if defined(CONFIG_SYNC) + +/** + * Creates a sync fence tracker and a sync fence. Adds sync fence tracker to Timeline system and + * returns sync fence. The sync fence will be signaled when the sync fence tracker is activated. + * + * @param timeline Timeline. + * @param point Point on timeline. + * @return Sync fence that will be signaled when tracker is activated. + */ +static struct sync_fence *mali_timeline_sync_fence_create_and_add_tracker(struct mali_timeline *timeline, mali_timeline_point point) +{ + struct mali_timeline_sync_fence_tracker *sync_fence_tracker; + struct sync_fence *sync_fence; + struct mali_timeline_fence fence; + + MALI_DEBUG_ASSERT_POINTER(timeline); + MALI_DEBUG_ASSERT(MALI_TIMELINE_NO_POINT != point); + + /* Allocate sync fence tracker. */ + sync_fence_tracker = _mali_osk_calloc(1, sizeof(struct mali_timeline_sync_fence_tracker)); + if (NULL == sync_fence_tracker) { + MALI_PRINT_ERROR(("Mali Timeline: sync_fence_tracker allocation failed\n")); + return NULL; + } + + /* Create sync flag. */ + MALI_DEBUG_ASSERT_POINTER(timeline->sync_tl); + sync_fence_tracker->flag = mali_sync_flag_create(timeline->sync_tl, point); + if (NULL == sync_fence_tracker->flag) { + MALI_PRINT_ERROR(("Mali Timeline: sync_flag creation failed\n")); + _mali_osk_free(sync_fence_tracker); + return NULL; + } + + /* Create sync fence from sync flag. */ + sync_fence = mali_sync_flag_create_fence(sync_fence_tracker->flag); + if (NULL == sync_fence) { + MALI_PRINT_ERROR(("Mali Timeline: sync_fence creation failed\n")); + mali_sync_flag_put(sync_fence_tracker->flag); + _mali_osk_free(sync_fence_tracker); + return NULL; + } + + /* Setup fence for tracker. */ + _mali_osk_memset(&fence, 0, sizeof(struct mali_timeline_fence)); + fence.sync_fd = -1; + fence.points[timeline->id] = point; + + /* Finally, add the tracker to Timeline system. */ + mali_timeline_tracker_init(&sync_fence_tracker->tracker, MALI_TIMELINE_TRACKER_SYNC, &fence, sync_fence_tracker); + point = mali_timeline_system_add_tracker(timeline->system, &sync_fence_tracker->tracker, MALI_TIMELINE_NONE); + MALI_DEBUG_ASSERT(MALI_TIMELINE_NO_POINT == point); + + return sync_fence; +} + +s32 mali_timeline_sync_fence_create(struct mali_timeline_system *system, struct mali_timeline_fence *fence) +{ + u32 i; + struct sync_fence *sync_fence_acc = NULL; + + MALI_DEBUG_ASSERT_POINTER(system); + MALI_DEBUG_ASSERT_POINTER(fence); + + for (i = 0; i < MALI_TIMELINE_MAX; ++i) { + struct mali_timeline *timeline; + struct sync_fence *sync_fence; + + if (MALI_TIMELINE_NO_POINT == fence->points[i]) continue; + + timeline = system->timelines[i]; + MALI_DEBUG_ASSERT_POINTER(timeline); + + sync_fence = mali_timeline_sync_fence_create_and_add_tracker(timeline, fence->points[i]); + if (NULL == sync_fence) goto error; + + if (NULL != sync_fence_acc) { + /* Merge sync fences. */ + sync_fence_acc = mali_sync_fence_merge(sync_fence_acc, sync_fence); + if (NULL == sync_fence_acc) goto error; + } else { + /* This was the first sync fence created. */ + sync_fence_acc = sync_fence; + } + } + + if (-1 != fence->sync_fd) { + struct sync_fence *sync_fence; + + sync_fence = sync_fence_fdget(fence->sync_fd); + if (NULL == sync_fence) goto error; + + if (NULL != sync_fence_acc) { + sync_fence_acc = mali_sync_fence_merge(sync_fence_acc, sync_fence); + if (NULL == sync_fence_acc) goto error; + } else { + sync_fence_acc = sync_fence; + } + } + + if (NULL == sync_fence_acc) { + MALI_DEBUG_ASSERT_POINTER(system->signaled_sync_tl); + + /* There was nothing to wait on, so return an already signaled fence. */ + + sync_fence_acc = mali_sync_timeline_create_signaled_fence(system->signaled_sync_tl); + if (NULL == sync_fence_acc) goto error; + } + + /* Return file descriptor for the accumulated sync fence. */ + return mali_sync_fence_fd_alloc(sync_fence_acc); + +error: + if (NULL != sync_fence_acc) { + sync_fence_put(sync_fence_acc); + } + + return -1; +} + +void mali_timeline_sync_fence_activate(struct mali_timeline_sync_fence_tracker *sync_fence_tracker) +{ + mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY; + + MALI_DEBUG_ASSERT_POINTER(sync_fence_tracker); + MALI_DEBUG_ASSERT_POINTER(sync_fence_tracker->flag); + + MALI_DEBUG_PRINT(4, ("Mali Timeline: activation for sync fence tracker\n")); + + /* Signal flag and release reference. */ + mali_sync_flag_signal(sync_fence_tracker->flag, 0); + mali_sync_flag_put(sync_fence_tracker->flag); + + /* Nothing can wait on this tracker, so nothing to schedule after release. */ + schedule_mask = mali_timeline_tracker_release(&sync_fence_tracker->tracker); + MALI_DEBUG_ASSERT(MALI_SCHEDULER_MASK_EMPTY == schedule_mask); + + _mali_osk_free(sync_fence_tracker); +} + +#endif /* defined(CONFIG_SYNC) */ diff --git a/drivers/gpu/arm/mali/common/mali_timeline_sync_fence.h b/drivers/gpu/arm/mali/common/mali_timeline_sync_fence.h new file mode 100644 index 00000000000000..ec28019738e161 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_timeline_sync_fence.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_timeline_sync_fence.h + * + * This file contains code related to creating sync fences from timeline fences. + */ + +#ifndef __MALI_TIMELINE_SYNC_FENCE_H__ +#define __MALI_TIMELINE_SYNC_FENCE_H__ + +#include "mali_timeline.h" + +#if defined(CONFIG_SYNC) + +/** + * Sync fence tracker. + */ +struct mali_timeline_sync_fence_tracker { + struct mali_sync_flag *flag; /**< Sync flag used to connect tracker and sync fence. */ + struct mali_timeline_tracker tracker; /**< Timeline tracker. */ +}; + +/** + * Create a sync fence that will be signaled when @ref fence is signaled. + * + * @param system Timeline system. + * @param fence Fence to create sync fence from. + * @return File descriptor for new sync fence, or -1 on error. + */ +s32 mali_timeline_sync_fence_create(struct mali_timeline_system *system, struct mali_timeline_fence *fence); + +/** + * Used by the Timeline system to activate a sync fence tracker. + * + * @param sync_fence_tracker Sync fence tracker. + * + */ +void mali_timeline_sync_fence_activate(struct mali_timeline_sync_fence_tracker *sync_fence_tracker); + +#endif /* defined(CONFIG_SYNC) */ + +#endif /* __MALI_TIMELINE_SYNC_FENCE_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_ukk.h b/drivers/gpu/arm/mali/common/mali_ukk.h new file mode 100644 index 00000000000000..80ef64dc5e7706 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_ukk.h @@ -0,0 +1,614 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_ukk.h + * Defines the kernel-side interface of the user-kernel interface + */ + +#ifndef __MALI_UKK_H__ +#define __MALI_UKK_H__ + +#include "mali_osk.h" +#include "mali_uk_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup uddapi Unified Device Driver (UDD) APIs + * + * @{ + */ + +/** + * @addtogroup u_k_api UDD User/Kernel Interface (U/K) APIs + * + * - The _mali_uk functions are an abstraction of the interface to the device + * driver. On certain OSs, this would be implemented via the IOCTL interface. + * On other OSs, it could be via extension of some Device Driver Class, or + * direct function call for Bare metal/RTOSs. + * - It is important to note that: + * - The Device Driver has implemented the _mali_ukk set of functions + * - The Base Driver calls the corresponding set of _mali_uku functions. + * - What requires porting is solely the calling mechanism from User-side to + * Kernel-side, and propagating back the results. + * - Each U/K function is associated with a (group, number) pair from + * \ref _mali_uk_functions to make it possible for a common function in the + * Base Driver and Device Driver to route User/Kernel calls from/to the + * correct _mali_uk function. For example, in an IOCTL system, the IOCTL number + * would be formed based on the group and number assigned to the _mali_uk + * function, as listed in \ref _mali_uk_functions. On the user-side, each + * _mali_uku function would just make an IOCTL with the IOCTL-code being an + * encoded form of the (group, number) pair. On the kernel-side, the Device + * Driver's IOCTL handler decodes the IOCTL-code back into a (group, number) + * pair, and uses this to determine which corresponding _mali_ukk should be + * called. + * - Refer to \ref _mali_uk_functions for more information about this + * (group, number) pairing. + * - In a system where there is no distinction between user and kernel-side, + * the U/K interface may be implemented as:@code + * MALI_STATIC_INLINE _mali_osk_errcode_t _mali_uku_examplefunction( _mali_uk_examplefunction_s *args ) + * { + * return mali_ukk_examplefunction( args ); + * } + * @endcode + * - Therefore, all U/K calls behave \em as \em though they were direct + * function calls (but the \b implementation \em need \em not be a direct + * function calls) + * + * @note Naming the _mali_uk functions the same on both User and Kernel sides + * on non-RTOS systems causes debugging issues when setting breakpoints. In + * this case, it is not clear which function the breakpoint is put on. + * Therefore the _mali_uk functions in user space are prefixed with \c _mali_uku + * and in kernel space with \c _mali_ukk. The naming for the argument + * structures is unaffected. + * + * - The _mali_uk functions are synchronous. + * - Arguments to the _mali_uk functions are passed in a structure. The only + * parameter passed to the _mali_uk functions is a pointer to this structure. + * This first member of this structure, ctx, is a pointer to a context returned + * by _mali_uku_open(). For example:@code + * typedef struct + * { + * void *ctx; + * u32 number_of_cores; + * } _mali_uk_get_gp_number_of_cores_s; + * @endcode + * + * - Each _mali_uk function has its own argument structure named after the + * function. The argument is distinguished by the _s suffix. + * - The argument types are defined by the base driver and user-kernel + * interface. + * - All _mali_uk functions return a standard \ref _mali_osk_errcode_t. + * - Only arguments of type input or input/output need be initialized before + * calling a _mali_uk function. + * - Arguments of type output and input/output are only valid when the + * _mali_uk function returns \ref _MALI_OSK_ERR_OK. + * - The \c ctx member is always invalid after it has been used by a + * _mali_uk function, except for the context management functions + * + * + * \b Interface \b restrictions + * + * The requirements of the interface mean that an implementation of the + * User-kernel interface may do no 'real' work. For example, the following are + * illegal in the User-kernel implementation: + * - Calling functions necessary for operation on all systems, which would + * not otherwise get called on RTOS systems. + * - For example, a U/K interface that calls multiple _mali_ukk functions + * during one particular U/K call. This could not be achieved by the same code + * which uses direct function calls for the U/K interface. + * - Writing in values to the args members, when otherwise these members would + * not hold a useful value for a direct function call U/K interface. + * - For example, U/K interface implementation that take NULL members in + * their arguments structure from the user side, but those members are + * replaced with non-NULL values in the kernel-side of the U/K interface + * implementation. A scratch area for writing data is one such example. In this + * case, a direct function call U/K interface would segfault, because no code + * would be present to replace the NULL pointer with a meaningful pointer. + * - Note that we discourage the case where the U/K implementation changes + * a NULL argument member to non-NULL, and then the Device Driver code (outside + * of the U/K layer) re-checks this member for NULL, and corrects it when + * necessary. Whilst such code works even on direct function call U/K + * intefaces, it reduces the testing coverage of the Device Driver code. This + * is because we have no way of testing the NULL == value path on an OS + * implementation. + * + * A number of allowable examples exist where U/K interfaces do 'real' work: + * - The 'pointer switching' technique for \ref _mali_ukk_get_system_info + * - In this case, without the pointer switching on direct function call + * U/K interface, the Device Driver code still sees the same thing: a pointer + * to which it can write memory. This is because such a system has no + * distinction between a user and kernel pointer. + * - Writing an OS-specific value into the ukk_private member for + * _mali_ukk_mem_mmap(). + * - In this case, this value is passed around by Device Driver code, but + * its actual value is never checked. Device Driver code simply passes it from + * the U/K layer to the OSK layer, where it can be acted upon. In this case, + * \em some OS implementations of the U/K (_mali_ukk_mem_mmap()) and OSK + * (_mali_osk_mem_mapregion_init()) functions will collaborate on the + * meaning of ukk_private member. On other OSs, it may be unused by both + * U/K and OSK layers + * - Therefore, on error inside the U/K interface implementation itself, + * it will be as though the _mali_ukk function itself had failed, and cleaned + * up after itself. + * - Compare this to a direct function call U/K implementation, where all + * error cleanup is handled by the _mali_ukk function itself. The direct + * function call U/K interface implementation is automatically atomic. + * + * The last example highlights a consequence of all U/K interface + * implementations: they must be atomic with respect to the Device Driver code. + * And therefore, should Device Driver code succeed but the U/K implementation + * fail afterwards (but before return to user-space), then the U/K + * implementation must cause appropriate cleanup actions to preserve the + * atomicity of the interface. + * + * @{ + */ + + +/** @defgroup _mali_uk_context U/K Context management + * + * These functions allow for initialisation of the user-kernel interface once per process. + * + * Generally the context will store the OS specific object to communicate with the kernel device driver and further + * state information required by the specific implementation. The context is shareable among all threads in the caller process. + * + * On IOCTL systems, this is likely to be a file descriptor as a result of opening the kernel device driver. + * + * On a bare-metal/RTOS system with no distinction between kernel and + * user-space, the U/K interface simply calls the _mali_ukk variant of the + * function by direct function call. In this case, the context returned is the + * mali_session_data from _mali_ukk_open(). + * + * The kernel side implementations of the U/K interface expect the first member of the argument structure to + * be the context created by _mali_uku_open(). On some OS implementations, the meaning of this context + * will be different between user-side and kernel-side. In which case, the kernel-side will need to replace this context + * with the kernel-side equivalent, because user-side will not have access to kernel-side data. The context parameter + * in the argument structure therefore has to be of type input/output. + * + * It should be noted that the caller cannot reuse the \c ctx member of U/K + * argument structure after a U/K call, because it may be overwritten. Instead, + * the context handle must always be stored elsewhere, and copied into + * the appropriate U/K argument structure for each user-side call to + * the U/K interface. This is not usually a problem, since U/K argument + * structures are usually placed on the stack. + * + * @{ */ + +/** @brief Begin a new Mali Device Driver session + * + * This is used to obtain a per-process context handle for all future U/K calls. + * + * @param context pointer to storage to return a (void*)context handle. + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_open( void **context ); + +/** @brief End a Mali Device Driver session + * + * This should be called when the process no longer requires use of the Mali Device Driver. + * + * The context handle must not be used after it has been closed. + * + * @param context pointer to a stored (void*)context handle. + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_close( void **context ); + +/** @} */ /* end group _mali_uk_context */ + + +/** @addtogroup _mali_uk_core U/K Core + * + * The core functions provide the following functionality: + * - verify that the user and kernel API are compatible + * - retrieve information about the cores and memory banks in the system + * - wait for the result of jobs started on a core + * + * @{ */ + +/** @brief Waits for a job notification. + * + * Sleeps until notified or a timeout occurs. Returns information about the notification. + * + * @param args see _mali_uk_wait_for_notification_s in "mali_utgard_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_wait_for_notification( _mali_uk_wait_for_notification_s *args ); + +/** @brief Post a notification to the notification queue of this application. + * + * @param args see _mali_uk_post_notification_s in "mali_utgard_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_post_notification( _mali_uk_post_notification_s *args ); + +/** @brief Verifies if the user and kernel side of this API are compatible. + * + * @param args see _mali_uk_get_api_version_s in "mali_utgard_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_get_api_version( _mali_uk_get_api_version_s *args ); + +/** @brief Get the user space settings applicable for calling process. + * + * @param args see _mali_uk_get_user_settings_s in "mali_utgard_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_get_user_settings(_mali_uk_get_user_settings_s *args); + +/** @brief Get a user space setting applicable for calling process. + * + * @param args see _mali_uk_get_user_setting_s in "mali_utgard_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_get_user_setting(_mali_uk_get_user_setting_s *args); + +/* @brief Grant or deny high priority scheduling for this session. + * + * @param args see _mali_uk_request_high_priority_s in "mali_utgard_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_request_high_priority(_mali_uk_request_high_priority_s *args); + +/** @} */ /* end group _mali_uk_core */ + + +/** @addtogroup _mali_uk_memory U/K Memory + * + * The memory functions provide functionality with and without a Mali-MMU present. + * + * For Mali-MMU based systems, the following functionality is provided: + * - Initialize and terminate MALI virtual address space + * - Allocate/deallocate physical memory to a MALI virtual address range and map into/unmap from the + * current process address space + * - Map/unmap external physical memory into the MALI virtual address range + * + * For Mali-nonMMU based systems: + * - Allocate/deallocate MALI memory + * + * @{ */ + +/** @brief Map Mali Memory into the current user process + * + * Maps Mali memory into the current user process in a generic way. + * + * This function is to be used for Mali-MMU mode. The function is available in both Mali-MMU and Mali-nonMMU modes, + * but should not be called by a user process in Mali-nonMMU mode. + * + * The implementation and operation of _mali_ukk_mem_mmap() is dependant on whether the driver is built for Mali-MMU + * or Mali-nonMMU: + * - In the nonMMU case, _mali_ukk_mem_mmap() requires a physical address to be specified. For this reason, an OS U/K + * implementation should not allow this to be called from user-space. In any case, nonMMU implementations are + * inherently insecure, and so the overall impact is minimal. Mali-MMU mode should be used if security is desired. + * - In the MMU case, _mali_ukk_mem_mmap() the _mali_uk_mem_mmap_s::phys_addr + * member is used for the \em Mali-virtual address desired for the mapping. The + * implementation of _mali_ukk_mem_mmap() will allocate both the CPU-virtual + * and CPU-physical addresses, and can cope with mapping a contiguous virtual + * address range to a sequence of non-contiguous physical pages. In this case, + * the CPU-physical addresses are not communicated back to the user-side, as + * they are unnecsessary; the \em Mali-virtual address range must be used for + * programming Mali structures. + * + * In the second (MMU) case, _mali_ukk_mem_mmap() handles management of + * CPU-virtual and CPU-physical ranges, but the \em caller must manage the + * \em Mali-virtual address range from the user-side. + * + * @note Mali-virtual address ranges are entirely separate between processes. + * It is not possible for a process to accidentally corrupt another process' + * \em Mali-virtual address space. + * + * @param args see _mali_uk_mem_mmap_s in "mali_utgard_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_mem_mmap( _mali_uk_mem_mmap_s *args ); + +/** @brief Unmap Mali Memory from the current user process + * + * Unmaps Mali memory from the current user process in a generic way. This only operates on Mali memory supplied + * from _mali_ukk_mem_mmap(). + * + * @param args see _mali_uk_mem_munmap_s in "mali_utgard_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_mem_munmap( _mali_uk_mem_munmap_s *args ); + +/** @brief Determine the buffer size necessary for an MMU page table dump. + * @param args see _mali_uk_query_mmu_page_table_dump_size_s in mali_utgard_uk_types.h + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_query_mmu_page_table_dump_size( _mali_uk_query_mmu_page_table_dump_size_s *args ); +/** @brief Dump MMU Page tables. + * @param args see _mali_uk_dump_mmu_page_table_s in mali_utgard_uk_types.h + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_dump_mmu_page_table( _mali_uk_dump_mmu_page_table_s * args ); + +/** @brief Write user data to specified Mali memory without causing segfaults. + * @param args see _mali_uk_mem_write_safe_s in mali_utgard_uk_types.h + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_mem_write_safe( _mali_uk_mem_write_safe_s *args ); + +/** @brief Map a physically contiguous range of memory into Mali + * @param args see _mali_uk_map_external_mem_s in mali_utgard_uk_types.h + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_map_external_mem( _mali_uk_map_external_mem_s *args ); + +/** @brief Unmap a physically contiguous range of memory from Mali + * @param args see _mali_uk_unmap_external_mem_s in mali_utgard_uk_types.h + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_unmap_external_mem( _mali_uk_unmap_external_mem_s *args ); + +#if defined(CONFIG_MALI400_UMP) +/** @brief Map UMP memory into Mali + * @param args see _mali_uk_attach_ump_mem_s in mali_utgard_uk_types.h + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_attach_ump_mem( _mali_uk_attach_ump_mem_s *args ); +/** @brief Unmap UMP memory from Mali + * @param args see _mali_uk_release_ump_mem_s in mali_utgard_uk_types.h + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_release_ump_mem( _mali_uk_release_ump_mem_s *args ); +#endif /* CONFIG_MALI400_UMP */ + +/** @brief Determine virtual-to-physical mapping of a contiguous memory range + * (optional) + * + * This allows the user-side to do a virtual-to-physical address translation. + * In conjunction with _mali_uku_map_external_mem, this can be used to do + * direct rendering. + * + * This function will only succeed on a virtual range that is mapped into the + * current process, and that is contigious. + * + * If va is not page-aligned, then it is rounded down to the next page + * boundary. The remainer is added to size, such that ((u32)va)+size before + * rounding is equal to ((u32)va)+size after rounding. The rounded modified + * va and size will be written out into args on success. + * + * If the supplied size is zero, or not a multiple of the system's PAGE_SIZE, + * then size will be rounded up to the next multiple of PAGE_SIZE before + * translation occurs. The rounded up size will be written out into args on + * success. + * + * On most OSs, virtual-to-physical address translation is a priveledged + * function. Therefore, the implementer must validate the range supplied, to + * ensure they are not providing arbitrary virtual-to-physical address + * translations. While it is unlikely such a mechanism could be used to + * compromise the security of a system on its own, it is possible it could be + * combined with another small security risk to cause a much larger security + * risk. + * + * @note This is an optional part of the interface, and is only used by certain + * implementations of libEGL. If the platform layer in your libEGL + * implementation does not require Virtual-to-Physical address translation, + * then this function need not be implemented. A stub implementation should not + * be required either, as it would only be removed by the compiler's dead code + * elimination. + * + * @note if implemented, this function is entirely platform-dependant, and does + * not exist in common code. + * + * @param args see _mali_uk_va_to_mali_pa_s in "mali_utgard_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_va_to_mali_pa( _mali_uk_va_to_mali_pa_s * args ); + +/** @} */ /* end group _mali_uk_memory */ + + +/** @addtogroup _mali_uk_pp U/K Fragment Processor + * + * The Fragment Processor (aka PP (Pixel Processor)) functions provide the following functionality: + * - retrieving version of the fragment processors + * - determine number of fragment processors + * - starting a job on a fragment processor + * + * @{ */ + +/** @brief Issue a request to start a new job on a Fragment Processor. + * + * If the request fails args->status is set to _MALI_UK_START_JOB_NOT_STARTED_DO_REQUEUE and you can + * try to start the job again. + * + * An existing job could be returned for requeueing if the new job has a higher priority than a previously started job + * which the hardware hasn't actually started processing yet. In this case the new job will be started instead and the + * existing one returned, otherwise the new job is started and the status field args->status is set to + * _MALI_UK_START_JOB_STARTED. + * + * Job completion can be awaited with _mali_ukk_wait_for_notification(). + * + * @param ctx user-kernel context (mali_session) + * @param uargs see _mali_uk_pp_start_job_s in "mali_utgard_uk_types.h". Use _mali_osk_copy_from_user to retrieve data! + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_pp_start_job( void *ctx, _mali_uk_pp_start_job_s *uargs ); + +/** + * @brief Issue a request to start new jobs on both Vertex Processor and Fragment Processor. + * + * @note Will call into @ref _mali_ukk_pp_start_job and @ref _mali_ukk_gp_start_job. + * + * @param ctx user-kernel context (mali_session) + * @param uargs see _mali_uk_pp_and_gp_start_job_s in "mali_utgard_uk_types.h". Use _mali_osk_copy_from_user to retrieve data! + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_pp_and_gp_start_job( void *ctx, _mali_uk_pp_and_gp_start_job_s *uargs ); + +/** @brief Returns the number of Fragment Processors in the system + * + * @param args see _mali_uk_get_pp_number_of_cores_s in "mali_utgard_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_get_pp_number_of_cores( _mali_uk_get_pp_number_of_cores_s *args ); + +/** @brief Returns the version that all Fragment Processor cores are compatible with. + * + * This function may only be called when _mali_ukk_get_pp_number_of_cores() indicated at least one Fragment + * Processor core is available. + * + * @param args see _mali_uk_get_pp_core_version_s in "mali_utgard_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_get_pp_core_version( _mali_uk_get_pp_core_version_s *args ); + +/** @brief Disable Write-back unit(s) on specified job + * + * @param args see _mali_uk_get_pp_core_version_s in "mali_utgard_uk_types.h" + */ +void _mali_ukk_pp_job_disable_wb(_mali_uk_pp_disable_wb_s *args); + + +/** @} */ /* end group _mali_uk_pp */ + + +/** @addtogroup _mali_uk_gp U/K Vertex Processor + * + * The Vertex Processor (aka GP (Geometry Processor)) functions provide the following functionality: + * - retrieving version of the Vertex Processors + * - determine number of Vertex Processors available + * - starting a job on a Vertex Processor + * + * @{ */ + +/** @brief Issue a request to start a new job on a Vertex Processor. + * + * If the request fails args->status is set to _MALI_UK_START_JOB_NOT_STARTED_DO_REQUEUE and you can + * try to start the job again. + * + * An existing job could be returned for requeueing if the new job has a higher priority than a previously started job + * which the hardware hasn't actually started processing yet. In this case the new job will be started and the + * existing one returned, otherwise the new job is started and the status field args->status is set to + * _MALI_UK_START_JOB_STARTED. + * + * Job completion can be awaited with _mali_ukk_wait_for_notification(). + * + * @param ctx user-kernel context (mali_session) + * @param uargs see _mali_uk_gp_start_job_s in "mali_utgard_uk_types.h". Use _mali_osk_copy_from_user to retrieve data! + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_gp_start_job( void *ctx, _mali_uk_gp_start_job_s *uargs ); + +/** @brief Returns the number of Vertex Processors in the system. + * + * @param args see _mali_uk_get_gp_number_of_cores_s in "mali_utgard_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_get_gp_number_of_cores( _mali_uk_get_gp_number_of_cores_s *args ); + +/** @brief Returns the version that all Vertex Processor cores are compatible with. + * + * This function may only be called when _mali_uk_get_gp_number_of_cores() indicated at least one Vertex + * Processor core is available. + * + * @param args see _mali_uk_get_gp_core_version_s in "mali_utgard_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_get_gp_core_version( _mali_uk_get_gp_core_version_s *args ); + +/** @brief Resume or abort suspended Vertex Processor jobs. + * + * After receiving notification that a Vertex Processor job was suspended from + * _mali_ukk_wait_for_notification() you can use this function to resume or abort the job. + * + * @param args see _mali_uk_gp_suspend_response_s in "mali_utgard_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_gp_suspend_response( _mali_uk_gp_suspend_response_s *args ); + +/** @} */ /* end group _mali_uk_gp */ + +#if defined(CONFIG_MALI400_PROFILING) +/** @addtogroup _mali_uk_profiling U/K Timeline profiling module + * @{ */ + +/** @brief Start recording profiling events. + * + * @param args see _mali_uk_profiling_start_s in "mali_utgard_uk_types.h" + */ +_mali_osk_errcode_t _mali_ukk_profiling_start(_mali_uk_profiling_start_s *args); + +/** @brief Add event to profiling buffer. + * + * @param args see _mali_uk_profiling_add_event_s in "mali_utgard_uk_types.h" + */ +_mali_osk_errcode_t _mali_ukk_profiling_add_event(_mali_uk_profiling_add_event_s *args); + +/** @brief Stop recording profiling events. + * + * @param args see _mali_uk_profiling_stop_s in "mali_utgard_uk_types.h" + */ +_mali_osk_errcode_t _mali_ukk_profiling_stop(_mali_uk_profiling_stop_s *args); + +/** @brief Retrieve a recorded profiling event. + * + * @param args see _mali_uk_profiling_get_event_s in "mali_utgard_uk_types.h" + */ +_mali_osk_errcode_t _mali_ukk_profiling_get_event(_mali_uk_profiling_get_event_s *args); + +/** @brief Clear recorded profiling events. + * + * @param args see _mali_uk_profiling_clear_s in "mali_utgard_uk_types.h" + */ +_mali_osk_errcode_t _mali_ukk_profiling_clear(_mali_uk_profiling_clear_s *args); + +/** @} */ /* end group _mali_uk_profiling */ +#endif + +/** @addtogroup _mali_uk_vsync U/K VSYNC reporting module + * @{ */ + +/** @brief Report events related to vsync. + * + * @note Events should be reported when starting to wait for vsync and when the + * waiting is finished. This information can then be used in kernel space to + * complement the GPU utilization metric. + * + * @param args see _mali_uk_vsync_event_report_s in "mali_utgard_uk_types.h" + */ +_mali_osk_errcode_t _mali_ukk_vsync_event_report(_mali_uk_vsync_event_report_s *args); + +/** @} */ /* end group _mali_uk_vsync */ + +/** @addtogroup _mali_sw_counters_report U/K Software counter reporting + * @{ */ + +/** @brief Report software counters. + * + * @param args see _mali_uk_sw_counters_report_s in "mali_uk_types.h" + */ +_mali_osk_errcode_t _mali_ukk_sw_counters_report(_mali_uk_sw_counters_report_s *args); + +/** @} */ /* end group _mali_sw_counters_report */ + +/** @} */ /* end group u_k_api */ + +/** @} */ /* end group uddapi */ + +u32 _mali_ukk_report_memory_usage(void); + +u32 _mali_ukk_utilization_gp_pp(void); + +u32 _mali_ukk_utilization_gp(void); + +u32 _mali_ukk_utilization_pp(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_UKK_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_user_settings_db.c b/drivers/gpu/arm/mali/common/mali_user_settings_db.c new file mode 100644 index 00000000000000..e14c91ae4863f8 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_user_settings_db.c @@ -0,0 +1,146 @@ +/** + * Copyright (C) 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_uk_types.h" +#include "mali_user_settings_db.h" +#include "mali_session.h" + +static u32 mali_user_settings[_MALI_UK_USER_SETTING_MAX]; +const char *_mali_uk_user_setting_descriptions[] = _MALI_UK_USER_SETTING_DESCRIPTIONS; + +static void mali_user_settings_notify(_mali_uk_user_setting_t setting, u32 value) +{ + mali_bool done = MALI_FALSE; + + /* + * This function gets a bit complicated because we can't hold the session lock while + * allocating notification objects. + */ + + while (!done) { + u32 i; + u32 num_sessions_alloc; + u32 num_sessions_with_lock; + u32 used_notification_objects = 0; + _mali_osk_notification_t **notobjs; + + /* Pre allocate the number of notifications objects we need right now (might change after lock has been taken) */ + num_sessions_alloc = mali_session_get_count(); + if (0 == num_sessions_alloc) { + /* No sessions to report to */ + return; + } + + notobjs = (_mali_osk_notification_t **)_mali_osk_malloc(sizeof(_mali_osk_notification_t *) * num_sessions_alloc); + if (NULL == notobjs) { + MALI_PRINT_ERROR(("Failed to notify user space session about num PP core change (alloc failure)\n")); + return; + } + + for (i = 0; i < num_sessions_alloc; i++) { + notobjs[i] = _mali_osk_notification_create(_MALI_NOTIFICATION_SETTINGS_CHANGED, + sizeof(_mali_uk_settings_changed_s)); + if (NULL != notobjs[i]) { + _mali_uk_settings_changed_s *data; + data = notobjs[i]->result_buffer; + + data->setting = setting; + data->value = value; + } else { + MALI_PRINT_ERROR(("Failed to notify user space session about setting change (alloc failure %u)\n", i)); + } + } + + mali_session_lock(); + + /* number of sessions will not change while we hold the lock */ + num_sessions_with_lock = mali_session_get_count(); + + if (num_sessions_alloc >= num_sessions_with_lock) { + /* We have allocated enough notification objects for all the sessions atm */ + struct mali_session_data *session, *tmp; + MALI_SESSION_FOREACH(session, tmp, link) { + MALI_DEBUG_ASSERT(used_notification_objects < num_sessions_alloc); + if (NULL != notobjs[used_notification_objects]) { + mali_session_send_notification(session, notobjs[used_notification_objects]); + notobjs[used_notification_objects] = NULL; /* Don't track this notification object any more */ + } + used_notification_objects++; + } + done = MALI_TRUE; + } + + mali_session_unlock(); + + /* Delete any remaining/unused notification objects */ + for (; used_notification_objects < num_sessions_alloc; used_notification_objects++) { + if (NULL != notobjs[used_notification_objects]) { + _mali_osk_notification_delete(notobjs[used_notification_objects]); + } + } + + _mali_osk_free(notobjs); + } +} + +void mali_set_user_setting(_mali_uk_user_setting_t setting, u32 value) +{ + mali_bool notify = MALI_FALSE; + + if (setting >= _MALI_UK_USER_SETTING_MAX) { + MALI_DEBUG_PRINT_ERROR(("Invalid user setting %ud\n")); + return; + } + + if (mali_user_settings[setting] != value) { + notify = MALI_TRUE; + } + + mali_user_settings[setting] = value; + + if (notify) { + mali_user_settings_notify(setting, value); + } +} + +u32 mali_get_user_setting(_mali_uk_user_setting_t setting) +{ + if (setting >= _MALI_UK_USER_SETTING_MAX) { + return 0; + } + + return mali_user_settings[setting]; +} + +_mali_osk_errcode_t _mali_ukk_get_user_setting(_mali_uk_get_user_setting_s *args) +{ + _mali_uk_user_setting_t setting; + MALI_DEBUG_ASSERT_POINTER(args); + + setting = args->setting; + + if (_MALI_UK_USER_SETTING_MAX > setting) { + args->value = mali_user_settings[setting]; + return _MALI_OSK_ERR_OK; + } else { + return _MALI_OSK_ERR_INVALID_ARGS; + } +} + +_mali_osk_errcode_t _mali_ukk_get_user_settings(_mali_uk_get_user_settings_s *args) +{ + MALI_DEBUG_ASSERT_POINTER(args); + + _mali_osk_memcpy(args->settings, mali_user_settings, sizeof(mali_user_settings)); + + return _MALI_OSK_ERR_OK; +} diff --git a/drivers/gpu/arm/mali/common/mali_user_settings_db.h b/drivers/gpu/arm/mali/common/mali_user_settings_db.h new file mode 100644 index 00000000000000..0b5127392ff19d --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_user_settings_db.h @@ -0,0 +1,39 @@ +/** + * Copyright (C) 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_USER_SETTINGS_DB_H__ +#define __MALI_USER_SETTINGS_DB_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "mali_uk_types.h" + +/** @brief Set Mali user setting in DB + * + * Update the DB with a new value for \a setting. If the value is different from theprevious set value running sessions will be notified of the change. + * + * @param setting the setting to be changed + * @param value the new value to set + */ +void mali_set_user_setting(_mali_uk_user_setting_t setting, u32 value); + +/** @brief Get current Mali user setting value from DB + * + * @param setting the setting to extract + * @return the value of the selected setting + */ +u32 mali_get_user_setting(_mali_uk_user_setting_t setting); + +#ifdef __cplusplus +} +#endif +#endif /* __MALI_KERNEL_USER_SETTING__ */ diff --git a/drivers/gpu/arm/mali/include/linux/mali/mali_utgard.h b/drivers/gpu/arm/mali/include/linux/mali/mali_utgard.h new file mode 100644 index 00000000000000..b7a9133691b9f0 --- /dev/null +++ b/drivers/gpu/arm/mali/include/linux/mali/mali_utgard.h @@ -0,0 +1,418 @@ +/* + * Copyright (C) 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_utgard.h + * Defines types and interface exposed by the Mali Utgard device driver + */ + +#ifndef __MALI_UTGARD_H__ +#define __MALI_UTGARD_H__ + +#include "mali_osk_types.h" + +#define MALI_GPU_NAME_UTGARD "mali-utgard" + +/* Mali-200 */ + +#define MALI_GPU_RESOURCES_MALI200(base_addr, gp_irq, pp_irq, mmu_irq) \ + MALI_GPU_RESOURCE_PP(base_addr + 0x0000, pp_irq) \ + MALI_GPU_RESOURCE_GP(base_addr + 0x2000, gp_irq) \ + MALI_GPU_RESOURCE_MMU(base_addr + 0x3000, mmu_irq) + +/* Mali-300 */ + +#define MALI_GPU_RESOURCES_MALI300(base_addr, gp_irq, gp_mmu_irq, pp_irq, pp_mmu_irq) \ + MALI_GPU_RESOURCES_MALI400_MP1(base_addr, gp_irq, gp_mmu_irq, pp_irq, pp_mmu_irq) + +#define MALI_GPU_RESOURCES_MALI300_PMU(base_addr, gp_irq, gp_mmu_irq, pp_irq, pp_mmu_irq) \ + MALI_GPU_RESOURCES_MALI400_MP1_PMU(base_addr, gp_irq, gp_mmu_irq, pp_irq, pp_mmu_irq) + +/* Mali-400 */ + +#define MALI_GPU_RESOURCES_MALI400_MP1(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq) \ + MALI_GPU_RESOURCE_L2(base_addr + 0x1000) \ + MALI_GPU_RESOURCE_GP_WITH_MMU(base_addr + 0x0000, gp_irq, base_addr + 0x3000, gp_mmu_irq) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(0, base_addr + 0x8000, pp0_irq, base_addr + 0x4000, pp0_mmu_irq) + +#define MALI_GPU_RESOURCES_MALI400_MP1_PMU(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq) \ + MALI_GPU_RESOURCES_MALI400_MP1(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq) \ + MALI_GPU_RESOURCE_PMU(base_addr + 0x2000) + +#define MALI_GPU_RESOURCES_MALI400_MP2(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq) \ + MALI_GPU_RESOURCE_L2(base_addr + 0x1000) \ + MALI_GPU_RESOURCE_GP_WITH_MMU(base_addr + 0x0000, gp_irq, base_addr + 0x3000, gp_mmu_irq) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(0, base_addr + 0x8000, pp0_irq, base_addr + 0x4000, pp0_mmu_irq) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(1, base_addr + 0xA000, pp1_irq, base_addr + 0x5000, pp1_mmu_irq) + +#define MALI_GPU_RESOURCES_MALI400_MP2_PMU(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq) \ + MALI_GPU_RESOURCES_MALI400_MP2(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq) \ + MALI_GPU_RESOURCE_PMU(base_addr + 0x2000) + +#define MALI_GPU_RESOURCES_MALI400_MP3(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq) \ + MALI_GPU_RESOURCE_L2(base_addr + 0x1000) \ + MALI_GPU_RESOURCE_GP_WITH_MMU(base_addr + 0x0000, gp_irq, base_addr + 0x3000, gp_mmu_irq) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(0, base_addr + 0x8000, pp0_irq, base_addr + 0x4000, pp0_mmu_irq) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(1, base_addr + 0xA000, pp1_irq, base_addr + 0x5000, pp1_mmu_irq) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(2, base_addr + 0xC000, pp2_irq, base_addr + 0x6000, pp2_mmu_irq) + +#define MALI_GPU_RESOURCES_MALI400_MP3_PMU(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq) \ + MALI_GPU_RESOURCES_MALI400_MP3(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq) \ + MALI_GPU_RESOURCE_PMU(base_addr + 0x2000) + +#define MALI_GPU_RESOURCES_MALI400_MP4(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq, pp3_irq, pp3_mmu_irq) \ + MALI_GPU_RESOURCE_L2(base_addr + 0x1000) \ + MALI_GPU_RESOURCE_GP_WITH_MMU(base_addr + 0x0000, gp_irq, base_addr + 0x3000, gp_mmu_irq) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(0, base_addr + 0x8000, pp0_irq, base_addr + 0x4000, pp0_mmu_irq) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(1, base_addr + 0xA000, pp1_irq, base_addr + 0x5000, pp1_mmu_irq) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(2, base_addr + 0xC000, pp2_irq, base_addr + 0x6000, pp2_mmu_irq) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(3, base_addr + 0xE000, pp3_irq, base_addr + 0x7000, pp3_mmu_irq) + +#define MALI_GPU_RESOURCES_MALI400_MP4_PMU(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq, pp3_irq, pp3_mmu_irq) \ + MALI_GPU_RESOURCES_MALI400_MP4(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq, pp3_irq, pp3_mmu_irq) \ + MALI_GPU_RESOURCE_PMU(base_addr + 0x2000) + +/* Mali-450 */ +#define MALI_GPU_RESOURCES_MALI450_MP2(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp_bcast_irq) \ + MALI_GPU_RESOURCE_L2(base_addr + 0x10000) \ + MALI_GPU_RESOURCE_GP_WITH_MMU(base_addr + 0x00000, gp_irq, base_addr + 0x03000, gp_mmu_irq) \ + MALI_GPU_RESOURCE_L2(base_addr + 0x01000) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(0, base_addr + 0x08000, pp0_irq, base_addr + 0x04000, pp0_mmu_irq) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(1, base_addr + 0x0A000, pp1_irq, base_addr + 0x05000, pp1_mmu_irq) \ + MALI_GPU_RESOURCE_BCAST(base_addr + 0x13000) \ + MALI_GPU_RESOURCE_DLBU(base_addr + 0x14000) \ + MALI_GPU_RESOURCE_PP_BCAST(base_addr + 0x16000, pp_bcast_irq) \ + MALI_GPU_RESOURCE_PP_MMU_BCAST(base_addr + 0x15000) \ + MALI_GPU_RESOURCE_DMA(base_addr + 0x12000) + +#define MALI_GPU_RESOURCES_MALI450_MP2_PMU(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp_bcast_irq) \ + MALI_GPU_RESOURCES_MALI450_MP2(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp_bcast_irq) \ + MALI_GPU_RESOURCE_PMU(base_addr + 0x2000) \ + +#define MALI_GPU_RESOURCES_MALI450_MP3(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq, pp_bcast_irq) \ + MALI_GPU_RESOURCE_L2(base_addr + 0x10000) \ + MALI_GPU_RESOURCE_GP_WITH_MMU(base_addr + 0x00000, gp_irq, base_addr + 0x03000, gp_mmu_irq) \ + MALI_GPU_RESOURCE_L2(base_addr + 0x01000) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(0, base_addr + 0x08000, pp0_irq, base_addr + 0x04000, pp0_mmu_irq) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(1, base_addr + 0x0A000, pp1_irq, base_addr + 0x05000, pp1_mmu_irq) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(2, base_addr + 0x0C000, pp2_irq, base_addr + 0x06000, pp2_mmu_irq) \ + MALI_GPU_RESOURCE_BCAST(base_addr + 0x13000) \ + MALI_GPU_RESOURCE_DLBU(base_addr + 0x14000) \ + MALI_GPU_RESOURCE_PP_BCAST(base_addr + 0x16000, pp_bcast_irq) \ + MALI_GPU_RESOURCE_PP_MMU_BCAST(base_addr + 0x15000) + +#define MALI_GPU_RESOURCES_MALI450_MP3_PMU(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq, pp_bcast_irq) \ + MALI_GPU_RESOURCES_MALI450_MP3(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq, pp_bcast_irq) \ + MALI_GPU_RESOURCE_PMU(base_addr + 0x2000) \ + +#define MALI_GPU_RESOURCES_MALI450_MP4(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq, pp3_irq, pp3_mmu_irq, pp_bcast_irq) \ + MALI_GPU_RESOURCE_L2(base_addr + 0x10000) \ + MALI_GPU_RESOURCE_GP_WITH_MMU(base_addr + 0x00000, gp_irq, base_addr + 0x03000, gp_mmu_irq) \ + MALI_GPU_RESOURCE_L2(base_addr + 0x01000) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(0, base_addr + 0x08000, pp0_irq, base_addr + 0x04000, pp0_mmu_irq) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(1, base_addr + 0x0A000, pp1_irq, base_addr + 0x05000, pp1_mmu_irq) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(2, base_addr + 0x0C000, pp2_irq, base_addr + 0x06000, pp2_mmu_irq) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(3, base_addr + 0x0E000, pp3_irq, base_addr + 0x07000, pp3_mmu_irq) \ + MALI_GPU_RESOURCE_BCAST(base_addr + 0x13000) \ + MALI_GPU_RESOURCE_DLBU(base_addr + 0x14000) \ + MALI_GPU_RESOURCE_PP_BCAST(base_addr + 0x16000, pp_bcast_irq) \ + MALI_GPU_RESOURCE_PP_MMU_BCAST(base_addr + 0x15000) \ + MALI_GPU_RESOURCE_DMA(base_addr + 0x12000) + +#define MALI_GPU_RESOURCES_MALI450_MP4_PMU(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq, pp3_irq, pp3_mmu_irq, pp_bcast_irq) \ + MALI_GPU_RESOURCES_MALI450_MP4(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq, pp3_irq, pp3_mmu_irq, pp_bcast_irq) \ + MALI_GPU_RESOURCE_PMU(base_addr + 0x2000) \ + +#define MALI_GPU_RESOURCES_MALI450_MP6(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq, pp3_irq, pp3_mmu_irq, pp4_irq, pp4_mmu_irq, pp5_irq, pp5_mmu_irq, pp_bcast_irq) \ + MALI_GPU_RESOURCE_L2(base_addr + 0x10000) \ + MALI_GPU_RESOURCE_GP_WITH_MMU(base_addr + 0x00000, gp_irq, base_addr + 0x03000, gp_mmu_irq) \ + MALI_GPU_RESOURCE_L2(base_addr + 0x01000) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(0, base_addr + 0x08000, pp0_irq, base_addr + 0x04000, pp0_mmu_irq) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(1, base_addr + 0x0A000, pp1_irq, base_addr + 0x05000, pp1_mmu_irq) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(2, base_addr + 0x0C000, pp2_irq, base_addr + 0x06000, pp2_mmu_irq) \ + MALI_GPU_RESOURCE_L2(base_addr + 0x11000) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(3, base_addr + 0x28000, pp3_irq, base_addr + 0x1C000, pp3_mmu_irq) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(4, base_addr + 0x2A000, pp4_irq, base_addr + 0x1D000, pp4_mmu_irq) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(5, base_addr + 0x2C000, pp5_irq, base_addr + 0x1E000, pp5_mmu_irq) \ + MALI_GPU_RESOURCE_BCAST(base_addr + 0x13000) \ + MALI_GPU_RESOURCE_DLBU(base_addr + 0x14000) \ + MALI_GPU_RESOURCE_PP_BCAST(base_addr + 0x16000, pp_bcast_irq) \ + MALI_GPU_RESOURCE_PP_MMU_BCAST(base_addr + 0x15000) \ + MALI_GPU_RESOURCE_DMA(base_addr + 0x12000) + +#define MALI_GPU_RESOURCES_MALI450_MP6_PMU(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq, pp3_irq, pp3_mmu_irq, pp4_irq, pp4_mmu_irq, pp5_irq, pp5_mmu_irq, pp_bcast_irq) \ + MALI_GPU_RESOURCES_MALI450_MP6(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq, pp3_irq, pp3_mmu_irq, pp4_irq, pp4_mmu_irq, pp5_irq, pp5_mmu_irq, pp_bcast_irq) \ + MALI_GPU_RESOURCE_PMU(base_addr + 0x2000) \ + +#define MALI_GPU_RESOURCES_MALI450_MP8(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq, pp3_irq, pp3_mmu_irq, pp4_irq, pp4_mmu_irq, pp5_irq, pp5_mmu_irq, pp6_irq, pp6_mmu_irq, pp7_irq, pp7_mmu_irq, pp_bcast_irq) \ + MALI_GPU_RESOURCE_L2(base_addr + 0x10000) \ + MALI_GPU_RESOURCE_GP_WITH_MMU(base_addr + 0x00000, gp_irq, base_addr + 0x03000, gp_mmu_irq) \ + MALI_GPU_RESOURCE_L2(base_addr + 0x01000) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(0, base_addr + 0x08000, pp0_irq, base_addr + 0x04000, pp0_mmu_irq) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(1, base_addr + 0x0A000, pp1_irq, base_addr + 0x05000, pp1_mmu_irq) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(2, base_addr + 0x0C000, pp2_irq, base_addr + 0x06000, pp2_mmu_irq) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(3, base_addr + 0x0E000, pp3_irq, base_addr + 0x07000, pp3_mmu_irq) \ + MALI_GPU_RESOURCE_L2(base_addr + 0x11000) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(4, base_addr + 0x28000, pp4_irq, base_addr + 0x1C000, pp4_mmu_irq) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(5, base_addr + 0x2A000, pp5_irq, base_addr + 0x1D000, pp5_mmu_irq) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(6, base_addr + 0x2C000, pp6_irq, base_addr + 0x1E000, pp6_mmu_irq) \ + MALI_GPU_RESOURCE_PP_WITH_MMU(7, base_addr + 0x2E000, pp7_irq, base_addr + 0x1F000, pp7_mmu_irq) \ + MALI_GPU_RESOURCE_BCAST(base_addr + 0x13000) \ + MALI_GPU_RESOURCE_DLBU(base_addr + 0x14000) \ + MALI_GPU_RESOURCE_PP_BCAST(base_addr + 0x16000, pp_bcast_irq) \ + MALI_GPU_RESOURCE_PP_MMU_BCAST(base_addr + 0x15000) \ + MALI_GPU_RESOURCE_DMA(base_addr + 0x12000) + +#define MALI_GPU_RESOURCES_MALI450_MP8_PMU(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq, pp3_irq, pp3_mmu_irq, pp4_irq, pp4_mmu_irq, pp5_irq, pp5_mmu_irq, pp6_irq, pp6_mmu_irq, pp7_irq, pp7_mmu_irq, pp_bcast_irq) \ + MALI_GPU_RESOURCES_MALI450_MP8(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq, pp3_irq, pp3_mmu_irq, pp4_irq, pp4_mmu_irq, pp5_irq, pp5_mmu_irq, pp6_irq, pp6_mmu_irq, pp7_irq, pp7_mmu_irq, pp_bcast_irq) \ + MALI_GPU_RESOURCE_PMU(base_addr + 0x2000) \ + +#define MALI_GPU_RESOURCE_L2(addr) \ + { \ + .name = "Mali_L2", \ + .flags = IORESOURCE_MEM, \ + .start = addr, \ + .end = addr + 0x200, \ + }, + +#define MALI_GPU_RESOURCE_GP(gp_addr, gp_irq) \ + { \ + .name = "Mali_GP", \ + .flags = IORESOURCE_MEM, \ + .start = gp_addr, \ + .end = gp_addr + 0x100, \ + }, \ + { \ + .name = "Mali_GP_IRQ", \ + .flags = IORESOURCE_IRQ, \ + .start = gp_irq, \ + .end = gp_irq, \ + }, \ + +#define MALI_GPU_RESOURCE_GP_WITH_MMU(gp_addr, gp_irq, gp_mmu_addr, gp_mmu_irq) \ + { \ + .name = "Mali_GP", \ + .flags = IORESOURCE_MEM, \ + .start = gp_addr, \ + .end = gp_addr + 0x100, \ + }, \ + { \ + .name = "Mali_GP_IRQ", \ + .flags = IORESOURCE_IRQ, \ + .start = gp_irq, \ + .end = gp_irq, \ + }, \ + { \ + .name = "Mali_GP_MMU", \ + .flags = IORESOURCE_MEM, \ + .start = gp_mmu_addr, \ + .end = gp_mmu_addr + 0x100, \ + }, \ + { \ + .name = "Mali_GP_MMU_IRQ", \ + .flags = IORESOURCE_IRQ, \ + .start = gp_mmu_irq, \ + .end = gp_mmu_irq, \ + }, + +#define MALI_GPU_RESOURCE_PP(pp_addr, pp_irq) \ + { \ + .name = "Mali_PP", \ + .flags = IORESOURCE_MEM, \ + .start = pp_addr, \ + .end = pp_addr + 0x1100, \ + }, \ + { \ + .name = "Mali_PP_IRQ", \ + .flags = IORESOURCE_IRQ, \ + .start = pp_irq, \ + .end = pp_irq, \ + }, \ + +#define MALI_GPU_RESOURCE_PP_WITH_MMU(id, pp_addr, pp_irq, pp_mmu_addr, pp_mmu_irq) \ + { \ + .name = "Mali_PP" #id, \ + .flags = IORESOURCE_MEM, \ + .start = pp_addr, \ + .end = pp_addr + 0x1100, \ + }, \ + { \ + .name = "Mali_PP" #id "_IRQ", \ + .flags = IORESOURCE_IRQ, \ + .start = pp_irq, \ + .end = pp_irq, \ + }, \ + { \ + .name = "Mali_PP" #id "_MMU", \ + .flags = IORESOURCE_MEM, \ + .start = pp_mmu_addr, \ + .end = pp_mmu_addr + 0x100, \ + }, \ + { \ + .name = "Mali_PP" #id "_MMU_IRQ", \ + .flags = IORESOURCE_IRQ, \ + .start = pp_mmu_irq, \ + .end = pp_mmu_irq, \ + }, + +#define MALI_GPU_RESOURCE_MMU(mmu_addr, mmu_irq) \ + { \ + .name = "Mali_MMU", \ + .flags = IORESOURCE_MEM, \ + .start = mmu_addr, \ + .end = mmu_addr + 0x100, \ + }, \ + { \ + .name = "Mali_MMU_IRQ", \ + .flags = IORESOURCE_IRQ, \ + .start = mmu_irq, \ + .end = mmu_irq, \ + }, + +#define MALI_GPU_RESOURCE_PMU(pmu_addr) \ + { \ + .name = "Mali_PMU", \ + .flags = IORESOURCE_MEM, \ + .start = pmu_addr, \ + .end = pmu_addr + 0x100, \ + }, + +#define MALI_GPU_RESOURCE_DMA(dma_addr) \ + { \ + .name = "Mali_DMA", \ + .flags = IORESOURCE_MEM, \ + .start = dma_addr, \ + .end = dma_addr + 0x100, \ + }, + +#define MALI_GPU_RESOURCE_DLBU(dlbu_addr) \ + { \ + .name = "Mali_DLBU", \ + .flags = IORESOURCE_MEM, \ + .start = dlbu_addr, \ + .end = dlbu_addr + 0x100, \ + }, + +#define MALI_GPU_RESOURCE_BCAST(bcast_addr) \ + { \ + .name = "Mali_Broadcast", \ + .flags = IORESOURCE_MEM, \ + .start = bcast_addr, \ + .end = bcast_addr + 0x100, \ + }, + +#define MALI_GPU_RESOURCE_PP_BCAST(pp_addr, pp_irq) \ + { \ + .name = "Mali_PP_Broadcast", \ + .flags = IORESOURCE_MEM, \ + .start = pp_addr, \ + .end = pp_addr + 0x1100, \ + }, \ + { \ + .name = "Mali_PP_Broadcast_IRQ", \ + .flags = IORESOURCE_IRQ, \ + .start = pp_irq, \ + .end = pp_irq, \ + }, \ + +#define MALI_GPU_RESOURCE_PP_MMU_BCAST(pp_mmu_bcast_addr) \ + { \ + .name = "Mali_PP_MMU_Broadcast", \ + .flags = IORESOURCE_MEM, \ + .start = pp_mmu_bcast_addr, \ + .end = pp_mmu_bcast_addr + 0x100, \ + }, + +struct mali_gpu_utilization_data { + unsigned int utilization_gpu; /* Utilization for GP and all PP cores combined, 0 = no utilization, 256 = full utilization */ + unsigned int utilization_gp; /* Utilization for GP core only, 0 = no utilization, 256 = full utilization */ + unsigned int utilization_pp; /* Utilization for all PP cores combined, 0 = no utilization, 256 = full utilization */ +#if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY) + unsigned int number_of_window_jobs; + unsigned int number_of_window_jobs_under_pressure; +#endif +}; + +struct mali_gpu_device_data { + /* Dedicated GPU memory range (physical). */ + unsigned long dedicated_mem_start; + unsigned long dedicated_mem_size; + + /* Shared GPU memory */ + unsigned long shared_mem_size; + + /* Frame buffer memory to be accessible by Mali GPU (physical) */ + unsigned long fb_start; + unsigned long fb_size; + + /* Max runtime [ms] for jobs */ + int max_job_runtime; + + /* Report GPU utilization in this interval (specified in ms) */ + unsigned long utilization_interval; + + /* Function that will receive periodic GPU utilization numbers */ + void (*utilization_callback)(struct mali_gpu_utilization_data *data); + + /* + * Mali PMU switch delay. + * Only needed if the power gates are connected to the PMU in a high fanout + * network. This value is the number of Mali clock cycles it takes to + * enable the power gates and turn on the power mesh. + * This value will have no effect if a daisy chain implementation is used. + */ + u32 pmu_switch_delay; + + + /* Mali Dynamic power domain configuration in sequence from 0-11 + * GP PP0 PP1 PP2 PP3 PP4 PP5 PP6 PP7, L2$0 L2$1 L2$2 + */ + u16 pmu_domain_config[12]; + + /* Fuction that platform callback for freq tunning, needed when POWER_PERFORMANCE_POLICY enabled*/ + int (*set_freq_callback)(unsigned int mhz); +}; + +/** @brief MALI GPU power down using MALI in-built PMU + * + * called to power down all cores + */ +int mali_pmu_powerdown(void); + + +/** @brief MALI GPU power up using MALI in-built PMU + * + * called to power up all cores + */ +int mali_pmu_powerup(void); + +/** + * Pause the scheduling and power state changes of Mali device driver. + * mali_dev_resume() must always be called as soon as possible after this function + * in order to resume normal operation of the Mali driver. + */ +void mali_dev_pause(void); + +/** + * Resume scheduling and allow power changes in Mali device driver. + * This must always be called after mali_dev_pause(). + */ +void mali_dev_resume(void); + +/** @brief Set the desired number of PP cores to use. + * + * The internal Mali PMU will be used, if present, to physically power off the PP cores. + * + * @param num_cores The number of desired cores + * @return 0 on success, otherwise error. -EINVAL means an invalid number of cores was specified. + */ +int mali_perf_set_num_pp_cores(unsigned int num_cores); + +#endif diff --git a/drivers/gpu/arm/mali/include/linux/mali/mali_utgard_counters.h b/drivers/gpu/arm/mali/include/linux/mali/mali_utgard_counters.h new file mode 100644 index 00000000000000..5da13c838da561 --- /dev/null +++ b/drivers/gpu/arm/mali/include/linux/mali/mali_utgard_counters.h @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _MALI_UTGARD_COUNTERS_H_ +#define _MALI_UTGARD_COUNTERS_H_ + +typedef struct { + void *unused; +} mali_cinstr_counter_info; + +typedef enum { + MALI_CINSTR_COUNTER_SOURCE_EGL = 0, + MALI_CINSTR_COUNTER_SOURCE_OPENGLES = 1000, + MALI_CINSTR_COUNTER_SOURCE_OPENVG = 2000, + MALI_CINSTR_COUNTER_SOURCE_GP = 3000, + MALI_CINSTR_COUNTER_SOURCE_PP = 4000, +} cinstr_counter_source; + +#define MALI_CINSTR_EGL_FIRST_COUNTER MALI_CINSTR_COUNTER_SOURCE_EGL +#define MALI_CINSTR_EGL_LAST_COUNTER (MALI_CINSTR_COUNTER_SOURCE_EGL + 999) + +#define MALI_CINSTR_GLES_FIRST_COUNTER MALI_CINSTR_COUNTER_SOURCE_OPENGLES +#define MALI_CINSTR_GLES_LAST_COUNTER (MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 999) + +#define MALI_CINSTR_VG_FIRST_COUNTER MALI_CINSTR_COUNTER_SOURCE_OPENVG +#define MALI_CINSTR_VG_LAST_COUNTER (MALI_CINSTR_COUNTER_SOURCE_OPENVG + 999) + +#define MALI_CINSTR_GP_FIRST_COUNTER MALI_CINSTR_COUNTER_SOURCE_GP +#define MALI_CINSTR_GP_LAST_COUNTER (MALI_CINSTR_COUNTER_SOURCE_GP + 999) + +#define MALI_CINSTR_PP_FIRST_COUNTER MALI_CINSTR_COUNTER_SOURCE_PP +#define MALI_CINSTR_PP_LAST_COUNTER (MALI_CINSTR_COUNTER_SOURCE_PP + 999) + + +typedef enum { + /* EGL counters */ + + MALI_CINSTR_EGL_BLIT_TIME = MALI_CINSTR_COUNTER_SOURCE_EGL + 0, + + /* Last counter in the EGL set */ + MALI_CINSTR_EGL_MAX_COUNTER = MALI_CINSTR_COUNTER_SOURCE_EGL + 1, + + /* GLES counters */ + + MALI_CINSTR_GLES_DRAW_ELEMENTS_CALLS = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 0, + MALI_CINSTR_GLES_DRAW_ELEMENTS_NUM_INDICES = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 1, + MALI_CINSTR_GLES_DRAW_ELEMENTS_NUM_TRANSFORMED = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 2, + MALI_CINSTR_GLES_DRAW_ARRAYS_CALLS = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 3, + MALI_CINSTR_GLES_DRAW_ARRAYS_NUM_TRANSFORMED = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 4, + MALI_CINSTR_GLES_DRAW_POINTS = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 5, + MALI_CINSTR_GLES_DRAW_LINES = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 6, + MALI_CINSTR_GLES_DRAW_LINE_LOOP = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 7, + MALI_CINSTR_GLES_DRAW_LINE_STRIP = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 8, + MALI_CINSTR_GLES_DRAW_TRIANGLES = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 9, + MALI_CINSTR_GLES_DRAW_TRIANGLE_STRIP = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 10, + MALI_CINSTR_GLES_DRAW_TRIANGLE_FAN = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 11, + MALI_CINSTR_GLES_NON_VBO_DATA_COPY_TIME = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 12, + MALI_CINSTR_GLES_UNIFORM_BYTES_COPIED_TO_MALI = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 13, + MALI_CINSTR_GLES_UPLOAD_TEXTURE_TIME = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 14, + MALI_CINSTR_GLES_UPLOAD_VBO_TIME = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 15, + MALI_CINSTR_GLES_NUM_FLUSHES = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 16, + MALI_CINSTR_GLES_NUM_VSHADERS_GENERATED = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 17, + MALI_CINSTR_GLES_NUM_FSHADERS_GENERATED = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 18, + MALI_CINSTR_GLES_VSHADER_GEN_TIME = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 19, + MALI_CINSTR_GLES_FSHADER_GEN_TIME = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 20, + MALI_CINSTR_GLES_INPUT_TRIANGLES = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 21, + MALI_CINSTR_GLES_VXCACHE_HIT = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 22, + MALI_CINSTR_GLES_VXCACHE_MISS = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 23, + MALI_CINSTR_GLES_VXCACHE_COLLISION = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 24, + MALI_CINSTR_GLES_CULLED_TRIANGLES = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 25, + MALI_CINSTR_GLES_CULLED_LINES = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 26, + MALI_CINSTR_GLES_BACKFACE_TRIANGLES = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 27, + MALI_CINSTR_GLES_GBCLIP_TRIANGLES = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 28, + MALI_CINSTR_GLES_GBCLIP_LINES = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 29, + MALI_CINSTR_GLES_TRIANGLES_DRAWN = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 30, + MALI_CINSTR_GLES_DRAWCALL_TIME = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 31, + MALI_CINSTR_GLES_TRIANGLES_COUNT = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 32, + MALI_CINSTR_GLES_INDEPENDENT_TRIANGLES_COUNT = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 33, + MALI_CINSTR_GLES_STRIP_TRIANGLES_COUNT = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 34, + MALI_CINSTR_GLES_FAN_TRIANGLES_COUNT = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 35, + MALI_CINSTR_GLES_LINES_COUNT = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 36, + MALI_CINSTR_GLES_INDEPENDENT_LINES_COUNT = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 37, + MALI_CINSTR_GLES_STRIP_LINES_COUNT = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 38, + MALI_CINSTR_GLES_LOOP_LINES_COUNT = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 39, + MALI_CINSTR_GLES_POINTS_COUNT = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 40, + + /* Last counter in the GLES set */ + MALI_CINSTR_GLES_MAX_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 41, + + /* OpenVG counters */ + + MALI_CINSTR_VG_MASK_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 0, + MALI_CINSTR_VG_CLEAR_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 1, + MALI_CINSTR_VG_APPEND_PATH_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 2, + MALI_CINSTR_VG_APPEND_PATH_DATA_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 3, + MALI_CINSTR_VG_MODIFY_PATH_COORDS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 4, + MALI_CINSTR_VG_TRANSFORM_PATH_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 5, + MALI_CINSTR_VG_INTERPOLATE_PATH_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 6, + MALI_CINSTR_VG_PATH_LENGTH_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 7, + MALI_CINSTR_VG_POINT_ALONG_PATH_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 8, + MALI_CINSTR_VG_PATH_BOUNDS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 9, + MALI_CINSTR_VG_PATH_TRANSFORMED_BOUNDS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 10, + MALI_CINSTR_VG_DRAW_PATH_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 11, + MALI_CINSTR_VG_CLEAR_IMAGE_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 12, + MALI_CINSTR_VG_IMAGE_SUB_DATA_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 13, + MALI_CINSTR_VG_GET_IMAGE_SUB_DATA_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 14, + MALI_CINSTR_VG_COPY_IMAGE_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 15, + MALI_CINSTR_VG_DRAW_IMAGE_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 16, + MALI_CINSTR_VG_SET_PIXELS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 17, + MALI_CINSTR_VG_WRITE_PIXELS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 18, + MALI_CINSTR_VG_GET_PIXELS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 19, + MALI_CINSTR_VG_READ_PIXELS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 20, + MALI_CINSTR_VG_COPY_PIXELS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 21, + MALI_CINSTR_VG_COLOR_MATRIX_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 22, + MALI_CINSTR_VG_CONVOLVE_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 23, + MALI_CINSTR_VG_SEPARABLE_CONVOLVE_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 24, + MALI_CINSTR_VG_GAUSSIAN_BLUR_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 25, + MALI_CINSTR_VG_LOOKUP_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 26, + MALI_CINSTR_VG_LOOKUP_SINGLE_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 27, + MALI_CINSTR_VG_CONTEXT_CREATE_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 28, + MALI_CINSTR_VG_STROKED_CUBICS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 29, + MALI_CINSTR_VG_STROKED_QUADS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 30, + MALI_CINSTR_VG_STROKED_ARCS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 31, + MALI_CINSTR_VG_STROKED_LINES_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 32, + MALI_CINSTR_VG_FILLED_CUBICS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 33, + MALI_CINSTR_VG_FILLED_QUADS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 34, + MALI_CINSTR_VG_FILLED_ARCS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 35, + MALI_CINSTR_VG_FILLED_LINES_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 36, + MALI_CINSTR_VG_DRAW_PATH_CALLS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 37, + MALI_CINSTR_VG_TRIANGLES_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 38, + MALI_CINSTR_VG_VERTICES_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 39, + MALI_CINSTR_VG_INDICES_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 40, + MALI_CINSTR_VG_FILLED_PATHS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 41, + MALI_CINSTR_VG_STROKED_PATHS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 42, + MALI_CINSTR_VG_FILL_EXTRACT_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 43, + MALI_CINSTR_VG_DRAW_FILLED_PATH_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 44, + MALI_CINSTR_VG_STROKE_EXTRACT_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 45, + MALI_CINSTR_VG_DRAW_STROKED_PATH_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 46, + MALI_CINSTR_VG_DRAW_PAINT_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 47, + MALI_CINSTR_VG_DATA_STRUCTURES_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 48, + MALI_CINSTR_VG_MEM_PATH_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 49, + MALI_CINSTR_VG_RSW_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 50, + + /* Last counter in the VG set */ + MALI_CINSTR_VG_MAX_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 51, + + /* Mali GP counters */ + + MALI_CINSTR_GP_DEPRECATED_0 = MALI_CINSTR_COUNTER_SOURCE_GP + 0, + MALI_CINSTR_GP_ACTIVE_CYCLES_GP = MALI_CINSTR_COUNTER_SOURCE_GP + 1, + MALI_CINSTR_GP_ACTIVE_CYCLES_VERTEX_SHADER = MALI_CINSTR_COUNTER_SOURCE_GP + 2, + MALI_CINSTR_GP_ACTIVE_CYCLES_VERTEX_STORER = MALI_CINSTR_COUNTER_SOURCE_GP + 3, + MALI_CINSTR_GP_ACTIVE_CYCLES_VERTEX_LOADER = MALI_CINSTR_COUNTER_SOURCE_GP + 4, + MALI_CINSTR_GP_CYCLES_VERTEX_LOADER_WAITING_FOR_VERTEX_SHADER = MALI_CINSTR_COUNTER_SOURCE_GP + 5, + MALI_CINSTR_GP_NUMBER_OF_WORDS_READ = MALI_CINSTR_COUNTER_SOURCE_GP + 6, + MALI_CINSTR_GP_NUMBER_OF_WORDS_WRITTEN = MALI_CINSTR_COUNTER_SOURCE_GP + 7, + MALI_CINSTR_GP_NUMBER_OF_READ_BURSTS = MALI_CINSTR_COUNTER_SOURCE_GP + 8, + MALI_CINSTR_GP_NUMBER_OF_WRITE_BURSTS = MALI_CINSTR_COUNTER_SOURCE_GP + 9, + MALI_CINSTR_GP_NUMBER_OF_VERTICES_PROCESSED = MALI_CINSTR_COUNTER_SOURCE_GP + 10, + MALI_CINSTR_GP_NUMBER_OF_VERTICES_FETCHED = MALI_CINSTR_COUNTER_SOURCE_GP + 11, + MALI_CINSTR_GP_NUMBER_OF_PRIMITIVES_FETCHED = MALI_CINSTR_COUNTER_SOURCE_GP + 12, + MALI_CINSTR_GP_RESERVED_13 = MALI_CINSTR_COUNTER_SOURCE_GP + 13, + MALI_CINSTR_GP_NUMBER_OF_BACKFACE_CULLINGS_DONE = MALI_CINSTR_COUNTER_SOURCE_GP + 14, + MALI_CINSTR_GP_NUMBER_OF_COMMANDS_WRITTEN_TO_TILES = MALI_CINSTR_COUNTER_SOURCE_GP + 15, + MALI_CINSTR_GP_NUMBER_OF_MEMORY_BLOCKS_ALLOCATED = MALI_CINSTR_COUNTER_SOURCE_GP + 16, + MALI_CINSTR_GP_RESERVED_17 = MALI_CINSTR_COUNTER_SOURCE_GP + 17, + MALI_CINSTR_GP_RESERVED_18 = MALI_CINSTR_COUNTER_SOURCE_GP + 18, + MALI_CINSTR_GP_NUMBER_OF_VERTEX_LOADER_CACHE_MISSES = MALI_CINSTR_COUNTER_SOURCE_GP + 19, + MALI_CINSTR_GP_RESERVED_20 = MALI_CINSTR_COUNTER_SOURCE_GP + 20, + MALI_CINSTR_GP_RESERVED_21 = MALI_CINSTR_COUNTER_SOURCE_GP + 21, + MALI_CINSTR_GP_ACTIVE_CYCLES_VERTEX_SHADER_COMMAND_PROCESSOR = MALI_CINSTR_COUNTER_SOURCE_GP + 22, + MALI_CINSTR_GP_ACTIVE_CYCLES_PLBU_COMMAND_PROCESSOR = MALI_CINSTR_COUNTER_SOURCE_GP + 23, + MALI_CINSTR_GP_ACTIVE_CYCLES_PLBU_LIST_WRITER = MALI_CINSTR_COUNTER_SOURCE_GP + 24, + MALI_CINSTR_GP_ACTIVE_CYCLES_THROUGH_THE_PREPARE_LIST_COMMANDS = MALI_CINSTR_COUNTER_SOURCE_GP + 25, + MALI_CINSTR_GP_RESERVED_26 = MALI_CINSTR_COUNTER_SOURCE_GP + 26, + MALI_CINSTR_GP_ACTIVE_CYCLES_PRIMITIVE_ASSEMBLY = MALI_CINSTR_COUNTER_SOURCE_GP + 27, + MALI_CINSTR_GP_ACTIVE_CYCLES_PLBU_VERTEX_FETCHER = MALI_CINSTR_COUNTER_SOURCE_GP + 28, + MALI_CINSTR_GP_RESERVED_29 = MALI_CINSTR_COUNTER_SOURCE_GP + 29, + MALI_CINSTR_GP_ACTIVE_CYCLES_BOUNDINGBOX_AND_COMMAND_GENERATOR = MALI_CINSTR_COUNTER_SOURCE_GP + 30, + MALI_CINSTR_GP_RESERVED_31 = MALI_CINSTR_COUNTER_SOURCE_GP + 31, + MALI_CINSTR_GP_ACTIVE_CYCLES_SCISSOR_TILE_ITERATOR = MALI_CINSTR_COUNTER_SOURCE_GP + 32, + MALI_CINSTR_GP_ACTIVE_CYCLES_PLBU_TILE_ITERATOR = MALI_CINSTR_COUNTER_SOURCE_GP + 33, + MALI_CINSTR_GP_JOB_COUNT = MALI_CINSTR_COUNTER_SOURCE_GP + 900, + + /* Mali PP counters */ + + MALI_CINSTR_PP_ACTIVE_CLOCK_CYCLES_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 0, + MALI_CINSTR_PP_TOTAL_CLOCK_CYCLES_COUNT_REMOVED = MALI_CINSTR_COUNTER_SOURCE_PP + 1, + MALI_CINSTR_PP_TOTAL_BUS_READS = MALI_CINSTR_COUNTER_SOURCE_PP + 2, + MALI_CINSTR_PP_TOTAL_BUS_WRITES = MALI_CINSTR_COUNTER_SOURCE_PP + 3, + MALI_CINSTR_PP_BUS_READ_REQUEST_CYCLES_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 4, + MALI_CINSTR_PP_BUS_WRITE_REQUEST_CYCLES_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 5, + MALI_CINSTR_PP_BUS_READ_TRANSACTIONS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 6, + MALI_CINSTR_PP_BUS_WRITE_TRANSACTIONS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 7, + MALI_CINSTR_PP_RESERVED_08 = MALI_CINSTR_COUNTER_SOURCE_PP + 8, + MALI_CINSTR_PP_TILE_WRITEBACK_WRITES = MALI_CINSTR_COUNTER_SOURCE_PP + 9, + MALI_CINSTR_PP_STORE_UNIT_WRITES = MALI_CINSTR_COUNTER_SOURCE_PP + 10, + MALI_CINSTR_PP_RESERVED_11 = MALI_CINSTR_COUNTER_SOURCE_PP + 11, + MALI_CINSTR_PP_PALETTE_CACHE_READS = MALI_CINSTR_COUNTER_SOURCE_PP + 12, + MALI_CINSTR_PP_TEXTURE_CACHE_UNCOMPRESSED_READS = MALI_CINSTR_COUNTER_SOURCE_PP + 13, + MALI_CINSTR_PP_POLYGON_LIST_READS = MALI_CINSTR_COUNTER_SOURCE_PP + 14, + MALI_CINSTR_PP_RSW_READS = MALI_CINSTR_COUNTER_SOURCE_PP + 15, + MALI_CINSTR_PP_VERTEX_CACHE_READS = MALI_CINSTR_COUNTER_SOURCE_PP + 16, + MALI_CINSTR_PP_UNIFORM_REMAPPING_READS = MALI_CINSTR_COUNTER_SOURCE_PP + 17, + MALI_CINSTR_PP_PROGRAM_CACHE_READS = MALI_CINSTR_COUNTER_SOURCE_PP + 18, + MALI_CINSTR_PP_VARYING_READS = MALI_CINSTR_COUNTER_SOURCE_PP + 19, + MALI_CINSTR_PP_TEXTURE_DESCRIPTORS_READS = MALI_CINSTR_COUNTER_SOURCE_PP + 20, + MALI_CINSTR_PP_TEXTURE_DESCRIPTORS_REMAPPING_READS = MALI_CINSTR_COUNTER_SOURCE_PP + 21, + MALI_CINSTR_PP_TEXTURE_CACHE_COMPRESSED_READS = MALI_CINSTR_COUNTER_SOURCE_PP + 22, + MALI_CINSTR_PP_LOAD_UNIT_READS = MALI_CINSTR_COUNTER_SOURCE_PP + 23, + MALI_CINSTR_PP_POLYGON_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 24, + MALI_CINSTR_PP_PIXEL_RECTANGLE_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 25, + MALI_CINSTR_PP_LINES_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 26, + MALI_CINSTR_PP_POINTS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 27, + MALI_CINSTR_PP_STALL_CYCLES_POLYGON_LIST_READER = MALI_CINSTR_COUNTER_SOURCE_PP + 28, + MALI_CINSTR_PP_STALL_CYCLES_TRIANGLE_SETUP = MALI_CINSTR_COUNTER_SOURCE_PP + 29, + MALI_CINSTR_PP_QUAD_RASTERIZED_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 30, + MALI_CINSTR_PP_FRAGMENT_RASTERIZED_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 31, + MALI_CINSTR_PP_FRAGMENT_REJECTED_FRAGMENT_KILL_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 32, + MALI_CINSTR_PP_FRAGMENT_REJECTED_FWD_FRAGMENT_KILL_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 33, + MALI_CINSTR_PP_FRAGMENT_PASSED_ZSTENCIL_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 34, + MALI_CINSTR_PP_PATCHES_REJECTED_EARLY_Z_STENCIL_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 35, + MALI_CINSTR_PP_PATCHES_EVALUATED = MALI_CINSTR_COUNTER_SOURCE_PP + 36, + MALI_CINSTR_PP_INSTRUCTION_COMPLETED_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 37, + MALI_CINSTR_PP_INSTRUCTION_FAILED_RENDEZVOUS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 38, + MALI_CINSTR_PP_INSTRUCTION_FAILED_VARYING_MISS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 39, + MALI_CINSTR_PP_INSTRUCTION_FAILED_TEXTURE_MISS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 40, + MALI_CINSTR_PP_INSTRUCTION_FAILED_LOAD_MISS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 41, + MALI_CINSTR_PP_INSTRUCTION_FAILED_TILE_READ_MISS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 42, + MALI_CINSTR_PP_INSTRUCTION_FAILED_STORE_MISS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 43, + MALI_CINSTR_PP_RENDEZVOUS_BREAKAGE_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 44, + MALI_CINSTR_PP_PIPELINE_BUBBLES_CYCLE_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 45, + MALI_CINSTR_PP_TEXTURE_MAPPER_MULTIPASS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 46, + MALI_CINSTR_PP_TEXTURE_MAPPER_CYCLE_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 47, + MALI_CINSTR_PP_VERTEX_CACHE_HIT_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 48, + MALI_CINSTR_PP_VERTEX_CACHE_MISS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 49, + MALI_CINSTR_PP_VARYING_CACHE_HIT_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 50, + MALI_CINSTR_PP_VARYING_CACHE_MISS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 51, + MALI_CINSTR_PP_VARYING_CACHE_CONFLICT_MISS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 52, + MALI_CINSTR_PP_TEXTURE_CACHE_HIT_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 53, + MALI_CINSTR_PP_TEXTURE_CACHE_MISS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 54, + MALI_CINSTR_PP_TEXTURE_CACHE_CONFLICT_MISS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 55, + MALI_CINSTR_PP_PALETTE_CACHE_HIT_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 56, /* Mali 200 only */ + MALI_CINSTR_PP_PALETTE_CACHE_MISS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 57, /* Mali 200 only */ + MALI_CINSTR_PP_COMPRESSED_TEXTURE_CACHE_HIT_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 56, /* Mali 400 class only */ + MALI_CINSTR_PP_COMPRESSED_TEXTURE_CACHE_MISS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 57, /* Mali 400 class only */ + MALI_CINSTR_PP_LOAD_STORE_CACHE_HIT_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 58, + MALI_CINSTR_PP_LOAD_STORE_CACHE_MISS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 59, + MALI_CINSTR_PP_PROGRAM_CACHE_HIT_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 60, + MALI_CINSTR_PP_PROGRAM_CACHE_MISS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 61, + MALI_CINSTR_PP_JOB_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 900, +} cinstr_counters_m200_t; + +#endif /*_MALI_UTGARD_COUNTERS_H_*/ diff --git a/drivers/gpu/arm/mali/include/linux/mali/mali_utgard_ioctl.h b/drivers/gpu/arm/mali/include/linux/mali/mali_utgard_ioctl.h new file mode 100644 index 00000000000000..f0ea2938121199 --- /dev/null +++ b/drivers/gpu/arm/mali/include/linux/mali/mali_utgard_ioctl.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_UTGARD_IOCTL_H__ +#define __MALI_UTGARD_IOCTL_H__ + +#include +#include +#include /* file system operations */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file mali_kernel_ioctl.h + * Interface to the Linux device driver. + * This file describes the interface needed to use the Linux device driver. + * Its interface is designed to used by the HAL implementation through a thin arch layer. + */ + +/** + * ioctl commands + */ + +#define MALI_IOC_BASE 0x82 +#define MALI_IOC_CORE_BASE (_MALI_UK_CORE_SUBSYSTEM + MALI_IOC_BASE) +#define MALI_IOC_MEMORY_BASE (_MALI_UK_MEMORY_SUBSYSTEM + MALI_IOC_BASE) +#define MALI_IOC_PP_BASE (_MALI_UK_PP_SUBSYSTEM + MALI_IOC_BASE) +#define MALI_IOC_GP_BASE (_MALI_UK_GP_SUBSYSTEM + MALI_IOC_BASE) +#define MALI_IOC_PROFILING_BASE (_MALI_UK_PROFILING_SUBSYSTEM + MALI_IOC_BASE) +#define MALI_IOC_VSYNC_BASE (_MALI_UK_VSYNC_SUBSYSTEM + MALI_IOC_BASE) + +#define MALI_IOC_WAIT_FOR_NOTIFICATION _IOWR(MALI_IOC_CORE_BASE, _MALI_UK_WAIT_FOR_NOTIFICATION, _mali_uk_wait_for_notification_s *) +#define MALI_IOC_GET_API_VERSION _IOWR(MALI_IOC_CORE_BASE, _MALI_UK_GET_API_VERSION, _mali_uk_get_api_version_s *) +#define MALI_IOC_POST_NOTIFICATION _IOWR(MALI_IOC_CORE_BASE, _MALI_UK_POST_NOTIFICATION, _mali_uk_post_notification_s *) +#define MALI_IOC_GET_USER_SETTING _IOWR(MALI_IOC_CORE_BASE, _MALI_UK_GET_USER_SETTING, _mali_uk_get_user_setting_s *) +#define MALI_IOC_GET_USER_SETTINGS _IOWR(MALI_IOC_CORE_BASE, _MALI_UK_GET_USER_SETTINGS, _mali_uk_get_user_settings_s *) +#define MALI_IOC_REQUEST_HIGH_PRIORITY _IOW (MALI_IOC_CORE_BASE, _MALI_UK_REQUEST_HIGH_PRIORITY, _mali_uk_request_high_priority_s *) +#define MALI_IOC_TIMELINE_GET_LATEST_POINT _IOWR(MALI_IOC_CORE_BASE, _MALI_UK_TIMELINE_GET_LATEST_POINT, _mali_uk_timeline_get_latest_point_s *) +#define MALI_IOC_TIMELINE_WAIT _IOWR(MALI_IOC_CORE_BASE, _MALI_UK_TIMELINE_WAIT, _mali_uk_timeline_wait_s *) +#define MALI_IOC_TIMELINE_CREATE_SYNC_FENCE _IOWR(MALI_IOC_CORE_BASE, _MALI_UK_TIMELINE_CREATE_SYNC_FENCE, _mali_uk_timeline_create_sync_fence_s *) +#define MALI_IOC_SOFT_JOB_START _IOWR(MALI_IOC_CORE_BASE, _MALI_UK_SOFT_JOB_START, _mali_uk_soft_job_start_s *) +#define MALI_IOC_SOFT_JOB_SIGNAL _IOWR(MALI_IOC_CORE_BASE, _MALI_UK_SOFT_JOB_SIGNAL, _mali_uk_soft_job_signal_s *) + +#define MALI_IOC_MEM_MAP_EXT _IOWR(MALI_IOC_MEMORY_BASE, _MALI_UK_MAP_EXT_MEM, _mali_uk_map_external_mem_s *) +#define MALI_IOC_MEM_UNMAP_EXT _IOW (MALI_IOC_MEMORY_BASE, _MALI_UK_UNMAP_EXT_MEM, _mali_uk_unmap_external_mem_s *) +#define MALI_IOC_MEM_ATTACH_DMA_BUF _IOWR(MALI_IOC_MEMORY_BASE, _MALI_UK_ATTACH_DMA_BUF, _mali_uk_attach_dma_buf_s *) +#define MALI_IOC_MEM_RELEASE_DMA_BUF _IOW(MALI_IOC_MEMORY_BASE, _MALI_UK_RELEASE_DMA_BUF, _mali_uk_release_dma_buf_s *) +#define MALI_IOC_MEM_DMA_BUF_GET_SIZE _IOR(MALI_IOC_MEMORY_BASE, _MALI_UK_DMA_BUF_GET_SIZE, _mali_uk_dma_buf_get_size_s *) +#define MALI_IOC_MEM_ATTACH_UMP _IOWR(MALI_IOC_MEMORY_BASE, _MALI_UK_ATTACH_UMP_MEM, _mali_uk_attach_ump_mem_s *) +#define MALI_IOC_MEM_RELEASE_UMP _IOW(MALI_IOC_MEMORY_BASE, _MALI_UK_RELEASE_UMP_MEM, _mali_uk_release_ump_mem_s *) +#define MALI_IOC_MEM_QUERY_MMU_PAGE_TABLE_DUMP_SIZE _IOR (MALI_IOC_MEMORY_BASE, _MALI_UK_QUERY_MMU_PAGE_TABLE_DUMP_SIZE, _mali_uk_query_mmu_page_table_dump_size_s *) +#define MALI_IOC_MEM_DUMP_MMU_PAGE_TABLE _IOWR(MALI_IOC_MEMORY_BASE, _MALI_UK_DUMP_MMU_PAGE_TABLE, _mali_uk_dump_mmu_page_table_s *) +#define MALI_IOC_MEM_WRITE_SAFE _IOWR(MALI_IOC_MEMORY_BASE, _MALI_UK_MEM_WRITE_SAFE, _mali_uk_mem_write_safe_s *) + +#define MALI_IOC_PP_START_JOB _IOWR(MALI_IOC_PP_BASE, _MALI_UK_PP_START_JOB, _mali_uk_pp_start_job_s *) +#define MALI_IOC_PP_AND_GP_START_JOB _IOWR(MALI_IOC_PP_BASE, _MALI_UK_PP_AND_GP_START_JOB, _mali_uk_pp_and_gp_start_job_s *) +#define MALI_IOC_PP_NUMBER_OF_CORES_GET _IOR (MALI_IOC_PP_BASE, _MALI_UK_GET_PP_NUMBER_OF_CORES, _mali_uk_get_pp_number_of_cores_s *) +#define MALI_IOC_PP_CORE_VERSION_GET _IOR (MALI_IOC_PP_BASE, _MALI_UK_GET_PP_CORE_VERSION, _mali_uk_get_pp_core_version_s * ) +#define MALI_IOC_PP_DISABLE_WB _IOW (MALI_IOC_PP_BASE, _MALI_UK_PP_DISABLE_WB, _mali_uk_pp_disable_wb_s * ) + +#define MALI_IOC_GP2_START_JOB _IOWR(MALI_IOC_GP_BASE, _MALI_UK_GP_START_JOB, _mali_uk_gp_start_job_s *) +#define MALI_IOC_GP2_NUMBER_OF_CORES_GET _IOR (MALI_IOC_GP_BASE, _MALI_UK_GET_GP_NUMBER_OF_CORES, _mali_uk_get_gp_number_of_cores_s *) +#define MALI_IOC_GP2_CORE_VERSION_GET _IOR (MALI_IOC_GP_BASE, _MALI_UK_GET_GP_CORE_VERSION, _mali_uk_get_gp_core_version_s *) +#define MALI_IOC_GP2_SUSPEND_RESPONSE _IOW (MALI_IOC_GP_BASE, _MALI_UK_GP_SUSPEND_RESPONSE,_mali_uk_gp_suspend_response_s *) + +#define MALI_IOC_PROFILING_START _IOWR(MALI_IOC_PROFILING_BASE, _MALI_UK_PROFILING_START, _mali_uk_profiling_start_s *) +#define MALI_IOC_PROFILING_ADD_EVENT _IOWR(MALI_IOC_PROFILING_BASE, _MALI_UK_PROFILING_ADD_EVENT, _mali_uk_profiling_add_event_s*) +#define MALI_IOC_PROFILING_STOP _IOWR(MALI_IOC_PROFILING_BASE, _MALI_UK_PROFILING_STOP, _mali_uk_profiling_stop_s *) +#define MALI_IOC_PROFILING_GET_EVENT _IOWR(MALI_IOC_PROFILING_BASE, _MALI_UK_PROFILING_GET_EVENT, _mali_uk_profiling_get_event_s *) +#define MALI_IOC_PROFILING_CLEAR _IOWR(MALI_IOC_PROFILING_BASE, _MALI_UK_PROFILING_CLEAR, _mali_uk_profiling_clear_s *) +#define MALI_IOC_PROFILING_GET_CONFIG _IOWR(MALI_IOC_PROFILING_BASE, _MALI_UK_PROFILING_GET_CONFIG, _mali_uk_get_user_settings_s *) +#define MALI_IOC_PROFILING_REPORT_SW_COUNTERS _IOW (MALI_IOC_PROFILING_BASE, _MALI_UK_PROFILING_REPORT_SW_COUNTERS, _mali_uk_sw_counters_report_s *) + +#define MALI_IOC_VSYNC_EVENT_REPORT _IOW (MALI_IOC_VSYNC_BASE, _MALI_UK_VSYNC_EVENT_REPORT, _mali_uk_vsync_event_report_s *) + +/* Deprecated ioctls */ +#define MALI_IOC_MEM_GET_BIG_BLOCK _IOWR(MALI_IOC_MEMORY_BASE, _MALI_UK_GET_BIG_BLOCK, void *) +#define MALI_IOC_MEM_FREE_BIG_BLOCK _IOW (MALI_IOC_MEMORY_BASE, _MALI_UK_FREE_BIG_BLOCK, void *) +#define MALI_IOC_MEM_INIT _IOR (MALI_IOC_MEMORY_BASE, _MALI_UK_INIT_MEM, void *) +#define MALI_IOC_MEM_TERM _IOW (MALI_IOC_MEMORY_BASE, _MALI_UK_TERM_MEM, void *) + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_UTGARD_IOCTL_H__ */ diff --git a/drivers/gpu/arm/mali/include/linux/mali/mali_utgard_profiling_events.h b/drivers/gpu/arm/mali/include/linux/mali/mali_utgard_profiling_events.h new file mode 100644 index 00000000000000..5cdd8eef0e25e2 --- /dev/null +++ b/drivers/gpu/arm/mali/include/linux/mali/mali_utgard_profiling_events.h @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _MALI_UTGARD_PROFILING_EVENTS_H_ +#define _MALI_UTGARD_PROFILING_EVENTS_H_ + +/* + * The event ID is a 32 bit value consisting of different fields + * reserved, 4 bits, for future use + * event type, 4 bits, cinstr_profiling_event_type_t + * event channel, 8 bits, the source of the event. + * event data, 16 bit field, data depending on event type + */ + +/** + * Specifies what kind of event this is + */ +typedef enum { + MALI_PROFILING_EVENT_TYPE_SINGLE = 0 << 24, + MALI_PROFILING_EVENT_TYPE_START = 1 << 24, + MALI_PROFILING_EVENT_TYPE_STOP = 2 << 24, + MALI_PROFILING_EVENT_TYPE_SUSPEND = 3 << 24, + MALI_PROFILING_EVENT_TYPE_RESUME = 4 << 24, +} cinstr_profiling_event_type_t; + + +/** + * Secifies the channel/source of the event + */ +typedef enum { + MALI_PROFILING_EVENT_CHANNEL_SOFTWARE = 0 << 16, + MALI_PROFILING_EVENT_CHANNEL_GP0 = 1 << 16, + MALI_PROFILING_EVENT_CHANNEL_PP0 = 5 << 16, + MALI_PROFILING_EVENT_CHANNEL_PP1 = 6 << 16, + MALI_PROFILING_EVENT_CHANNEL_PP2 = 7 << 16, + MALI_PROFILING_EVENT_CHANNEL_PP3 = 8 << 16, + MALI_PROFILING_EVENT_CHANNEL_PP4 = 9 << 16, + MALI_PROFILING_EVENT_CHANNEL_PP5 = 10 << 16, + MALI_PROFILING_EVENT_CHANNEL_PP6 = 11 << 16, + MALI_PROFILING_EVENT_CHANNEL_PP7 = 12 << 16, + MALI_PROFILING_EVENT_CHANNEL_GPU = 21 << 16, +} cinstr_profiling_event_channel_t; + + +#define MALI_PROFILING_MAKE_EVENT_CHANNEL_GP(num) (((MALI_PROFILING_EVENT_CHANNEL_GP0 >> 16) + (num)) << 16) +#define MALI_PROFILING_MAKE_EVENT_CHANNEL_PP(num) (((MALI_PROFILING_EVENT_CHANNEL_PP0 >> 16) + (num)) << 16) + +/** + * These events are applicable when the type MALI_PROFILING_EVENT_TYPE_SINGLE is used from software channel + */ +typedef enum { + MALI_PROFILING_EVENT_REASON_SINGLE_SW_NONE = 0, + MALI_PROFILING_EVENT_REASON_SINGLE_SW_EGL_NEW_FRAME = 1, + MALI_PROFILING_EVENT_REASON_SINGLE_SW_FLUSH = 2, + MALI_PROFILING_EVENT_REASON_SINGLE_SW_EGL_SWAP_BUFFERS = 3, + MALI_PROFILING_EVENT_REASON_SINGLE_SW_FB_EVENT = 4, + MALI_PROFILING_EVENT_REASON_SINGLE_SW_GP_ENQUEUE = 5, + MALI_PROFILING_EVENT_REASON_SINGLE_SW_PP_ENQUEUE = 6, + MALI_PROFILING_EVENT_REASON_SINGLE_SW_READBACK = 7, + MALI_PROFILING_EVENT_REASON_SINGLE_SW_WRITEBACK = 8, + MALI_PROFILING_EVENT_REASON_SINGLE_SW_ENTER_API_FUNC = 10, + MALI_PROFILING_EVENT_REASON_SINGLE_SW_LEAVE_API_FUNC = 11, + MALI_PROFILING_EVENT_REASON_SINGLE_SW_DISCARD_ATTACHMENTS = 13, + MALI_PROFILING_EVENT_REASON_SINGLE_SW_UMP_TRY_LOCK = 53, + MALI_PROFILING_EVENT_REASON_SINGLE_SW_UMP_LOCK = 54, + MALI_PROFILING_EVENT_REASON_SINGLE_SW_UMP_UNLOCK = 55, + MALI_PROFILING_EVENT_REASON_SINGLE_LOCK_CONTENDED = 56, + MALI_PROFILING_EVENT_REASON_SINGLE_SW_EGL_MALI_FENCE_DUP = 57, + MALI_PROFILING_EVENT_REASON_SINGLE_SW_EGL_SET_PP_JOB_FENCE = 58, + MALI_PROFILING_EVENT_REASON_SINGLE_SW_EGL_WAIT_SYNC = 59, + MALI_PROFILING_EVENT_REASON_SINGLE_SW_EGL_CREATE_FENCE_SYNC = 60, + MALI_PROFILING_EVENT_REASON_SINGLE_SW_EGL_CREATE_NATIVE_FENCE_SYNC = 61, + MALI_PROFILING_EVENT_REASON_SINGLE_SW_EGL_FENCE_FLUSH = 62, + MALI_PROFILING_EVENT_REASON_SINGLE_SW_EGL_FLUSH_SERVER_WAITS= 63, +} cinstr_profiling_event_reason_single_sw_t; + +/** + * These events are applicable when the type MALI_PROFILING_EVENT_TYPE_START/STOP is used from software channel + * to inform whether the core is physical or virtual + */ +typedef enum { + MALI_PROFILING_EVENT_REASON_START_STOP_HW_PHYSICAL = 0, + MALI_PROFILING_EVENT_REASON_START_STOP_HW_VIRTUAL = 1, +} cinstr_profiling_event_reason_start_stop_hw_t; + +/** + * These events are applicable when the type MALI_PROFILING_EVENT_TYPE_START/STOP is used from software channel + */ +typedef enum { + /*MALI_PROFILING_EVENT_REASON_START_STOP_SW_NONE = 0,*/ + MALI_PROFILING_EVENT_REASON_START_STOP_SW_MALI = 1, + MALI_PROFILING_EVENT_REASON_START_STOP_SW_CALLBACK_THREAD = 2, + MALI_PROFILING_EVENT_REASON_START_STOP_SW_WORKER_THREAD = 3, + MALI_PROFILING_EVENT_REASON_START_STOP_SW_BOTTOM_HALF = 4, + MALI_PROFILING_EVENT_REASON_START_STOP_SW_UPPER_HALF = 5, +} cinstr_profiling_event_reason_start_stop_sw_t; + +/** + * These events are applicable when the type MALI_PROFILING_EVENT_TYPE_SUSPEND/RESUME is used from software channel + */ +typedef enum { + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_NONE = 0, /* used */ + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_PIPELINE_FULL = 1, /* NOT used */ + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_VSYNC = 26, /* used in some build configurations */ + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_FB_IFRAME_WAIT = 27, /* USED */ + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_FB_IFRAME_SYNC = 28, /* USED */ + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_VG_WAIT_FILTER_CLEANUP = 29, /* used */ + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_VG_WAIT_TEXTURE = 30, /* used */ + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_GLES_WAIT_MIPLEVEL = 31, /* used */ + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_GLES_WAIT_READPIXELS = 32, /* used */ + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_EGL_WAIT_SWAP_IMMEDIATE = 33, /* NOT used */ + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_ICS_QUEUE_BUFFER = 34, /* USED */ + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_ICS_DEQUEUE_BUFFER = 35, /* USED */ + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_UMP_LOCK = 36, /* Not currently used */ + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_X11_GLOBAL_LOCK = 37, /* Not currently used */ + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_X11_SWAP = 38, /* Not currently used */ + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_MALI_EGL_IMAGE_SYNC_WAIT = 39, /* USED */ + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_GP_JOB_HANDLING = 40, /* USED */ + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_PP_JOB_HANDLING = 41, /* USED */ + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_EGL_MALI_FENCE_MERGE = 42, /* USED */ + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_EGL_MALI_FENCE_DUP = 43, + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_EGL_FLUSH_SERVER_WAITS = 44, + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_EGL_WAIT_SYNC = 45, /* USED */ +} cinstr_profiling_event_reason_suspend_resume_sw_t; + +/** + * These events are applicable when the type MALI_PROFILING_EVENT_TYPE_SINGLE is used from a HW channel (GPx+PPx) + */ +typedef enum { + MALI_PROFILING_EVENT_REASON_SINGLE_HW_NONE = 0, + MALI_PROFILING_EVENT_REASON_SINGLE_HW_INTERRUPT = 1, + MALI_PROFILING_EVENT_REASON_SINGLE_HW_FLUSH = 2, +} cinstr_profiling_event_reason_single_hw_t; + +/** + * These events are applicable when the type MALI_PROFILING_EVENT_TYPE_SINGLE is used from the GPU channel + */ +typedef enum { + MALI_PROFILING_EVENT_REASON_SINGLE_GPU_NONE = 0, + MALI_PROFILING_EVENT_REASON_SINGLE_GPU_FREQ_VOLT_CHANGE = 1, + MALI_PROFILING_EVENT_REASON_SINGLE_GPU_L20_COUNTERS = 2, + MALI_PROFILING_EVENT_REASON_SINGLE_GPU_L21_COUNTERS = 3, + MALI_PROFILING_EVENT_REASON_SINGLE_GPU_L22_COUNTERS = 4, +} cinstr_profiling_event_reason_single_gpu_t; + +/** + * These values are applicable for the 3rd data parameter when + * the type MALI_PROFILING_EVENT_TYPE_START is used from the software channel + * with the MALI_PROFILING_EVENT_REASON_START_STOP_BOTTOM_HALF reason. + */ +typedef enum { + MALI_PROFILING_EVENT_DATA_CORE_GP0 = 1, + MALI_PROFILING_EVENT_DATA_CORE_PP0 = 5, + MALI_PROFILING_EVENT_DATA_CORE_PP1 = 6, + MALI_PROFILING_EVENT_DATA_CORE_PP2 = 7, + MALI_PROFILING_EVENT_DATA_CORE_PP3 = 8, + MALI_PROFILING_EVENT_DATA_CORE_PP4 = 9, + MALI_PROFILING_EVENT_DATA_CORE_PP5 = 10, + MALI_PROFILING_EVENT_DATA_CORE_PP6 = 11, + MALI_PROFILING_EVENT_DATA_CORE_PP7 = 12, +} cinstr_profiling_event_data_core_t; + +#define MALI_PROFILING_MAKE_EVENT_DATA_CORE_GP(num) (MALI_PROFILING_EVENT_DATA_CORE_GP0 + (num)) +#define MALI_PROFILING_MAKE_EVENT_DATA_CORE_PP(num) (MALI_PROFILING_EVENT_DATA_CORE_PP0 + (num)) + + +#endif /*_MALI_UTGARD_PROFILING_EVENTS_H_*/ diff --git a/drivers/gpu/arm/mali/include/linux/mali/mali_utgard_profiling_gator_api.h b/drivers/gpu/arm/mali/include/linux/mali/mali_utgard_profiling_gator_api.h new file mode 100644 index 00000000000000..f8af3e49e9bbc2 --- /dev/null +++ b/drivers/gpu/arm/mali/include/linux/mali/mali_utgard_profiling_gator_api.h @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_UTGARD_PROFILING_GATOR_API_H__ +#define __MALI_UTGARD_PROFILING_GATOR_API_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define MALI_PROFILING_API_VERSION 4 + +#define MAX_NUM_L2_CACHE_CORES 3 +#define MAX_NUM_FP_CORES 8 +#define MAX_NUM_VP_CORES 1 + +/** The list of events supported by the Mali DDK. */ +typedef enum { + /* Vertex processor activity */ + ACTIVITY_VP_0 = 0, + + /* Fragment processor activity */ + ACTIVITY_FP_0, + ACTIVITY_FP_1, + ACTIVITY_FP_2, + ACTIVITY_FP_3, + ACTIVITY_FP_4, + ACTIVITY_FP_5, + ACTIVITY_FP_6, + ACTIVITY_FP_7, + + /* L2 cache counters */ + COUNTER_L2_0_C0, + COUNTER_L2_0_C1, + COUNTER_L2_1_C0, + COUNTER_L2_1_C1, + COUNTER_L2_2_C0, + COUNTER_L2_2_C1, + + /* Vertex processor counters */ + COUNTER_VP_0_C0, + COUNTER_VP_0_C1, + + /* Fragment processor counters */ + COUNTER_FP_0_C0, + COUNTER_FP_0_C1, + COUNTER_FP_1_C0, + COUNTER_FP_1_C1, + COUNTER_FP_2_C0, + COUNTER_FP_2_C1, + COUNTER_FP_3_C0, + COUNTER_FP_3_C1, + COUNTER_FP_4_C0, + COUNTER_FP_4_C1, + COUNTER_FP_5_C0, + COUNTER_FP_5_C1, + COUNTER_FP_6_C0, + COUNTER_FP_6_C1, + COUNTER_FP_7_C0, + COUNTER_FP_7_C1, + + /* + * If more hardware counters are added, the _mali_osk_hw_counter_table + * below should also be updated. + */ + + /* EGL software counters */ + COUNTER_EGL_BLIT_TIME, + + /* GLES software counters */ + COUNTER_GLES_DRAW_ELEMENTS_CALLS, + COUNTER_GLES_DRAW_ELEMENTS_NUM_INDICES, + COUNTER_GLES_DRAW_ELEMENTS_NUM_TRANSFORMED, + COUNTER_GLES_DRAW_ARRAYS_CALLS, + COUNTER_GLES_DRAW_ARRAYS_NUM_TRANSFORMED, + COUNTER_GLES_DRAW_POINTS, + COUNTER_GLES_DRAW_LINES, + COUNTER_GLES_DRAW_LINE_LOOP, + COUNTER_GLES_DRAW_LINE_STRIP, + COUNTER_GLES_DRAW_TRIANGLES, + COUNTER_GLES_DRAW_TRIANGLE_STRIP, + COUNTER_GLES_DRAW_TRIANGLE_FAN, + COUNTER_GLES_NON_VBO_DATA_COPY_TIME, + COUNTER_GLES_UNIFORM_BYTES_COPIED_TO_MALI, + COUNTER_GLES_UPLOAD_TEXTURE_TIME, + COUNTER_GLES_UPLOAD_VBO_TIME, + COUNTER_GLES_NUM_FLUSHES, + COUNTER_GLES_NUM_VSHADERS_GENERATED, + COUNTER_GLES_NUM_FSHADERS_GENERATED, + COUNTER_GLES_VSHADER_GEN_TIME, + COUNTER_GLES_FSHADER_GEN_TIME, + COUNTER_GLES_INPUT_TRIANGLES, + COUNTER_GLES_VXCACHE_HIT, + COUNTER_GLES_VXCACHE_MISS, + COUNTER_GLES_VXCACHE_COLLISION, + COUNTER_GLES_CULLED_TRIANGLES, + COUNTER_GLES_CULLED_LINES, + COUNTER_GLES_BACKFACE_TRIANGLES, + COUNTER_GLES_GBCLIP_TRIANGLES, + COUNTER_GLES_GBCLIP_LINES, + COUNTER_GLES_TRIANGLES_DRAWN, + COUNTER_GLES_DRAWCALL_TIME, + COUNTER_GLES_TRIANGLES_COUNT, + COUNTER_GLES_INDEPENDENT_TRIANGLES_COUNT, + COUNTER_GLES_STRIP_TRIANGLES_COUNT, + COUNTER_GLES_FAN_TRIANGLES_COUNT, + COUNTER_GLES_LINES_COUNT, + COUNTER_GLES_INDEPENDENT_LINES_COUNT, + COUNTER_GLES_STRIP_LINES_COUNT, + COUNTER_GLES_LOOP_LINES_COUNT, + + /* Framebuffer capture pseudo-counter */ + COUNTER_FILMSTRIP, + + NUMBER_OF_EVENTS +} _mali_osk_counter_id; + +#define FIRST_ACTIVITY_EVENT ACTIVITY_VP_0 +#define LAST_ACTIVITY_EVENT ACTIVITY_FP_7 + +#define FIRST_HW_COUNTER COUNTER_L2_0_C0 +#define LAST_HW_COUNTER COUNTER_FP_7_C1 + +#define FIRST_SW_COUNTER COUNTER_EGL_BLIT_TIME +#define LAST_SW_COUNTER COUNTER_GLES_LOOP_LINES_COUNT + +#define FIRST_SPECIAL_COUNTER COUNTER_FILMSTRIP +#define LAST_SPECIAL_COUNTER COUNTER_FILMSTRIP + +/** + * Structure to pass performance counter data of a Mali core + */ +typedef struct _mali_profiling_core_counters { + u32 source0; + u32 value0; + u32 source1; + u32 value1; +} _mali_profiling_core_counters; + +/** + * Structure to pass performance counter data of Mali L2 cache cores + */ +typedef struct _mali_profiling_l2_counter_values { + struct _mali_profiling_core_counters cores[MAX_NUM_L2_CACHE_CORES]; +} _mali_profiling_l2_counter_values; + +/** + * Structure to pass data defining Mali instance in use: + * + * mali_product_id - Mali product id + * mali_version_major - Mali version major number + * mali_version_minor - Mali version minor number + * num_of_l2_cores - number of L2 cache cores + * num_of_fp_cores - number of fragment processor cores + * num_of_vp_cores - number of vertex processor cores + */ +typedef struct _mali_profiling_mali_version { + u32 mali_product_id; + u32 mali_version_major; + u32 mali_version_minor; + u32 num_of_l2_cores; + u32 num_of_fp_cores; + u32 num_of_vp_cores; +} _mali_profiling_mali_version; + +/* + * List of possible actions to be controlled by Streamline. + * The following numbers are used by gator to control the frame buffer dumping and s/w counter reporting. + * We cannot use the enums in mali_uk_types.h because they are unknown inside gator. + */ +#define FBDUMP_CONTROL_ENABLE (1) +#define FBDUMP_CONTROL_RATE (2) +#define SW_COUNTER_ENABLE (3) +#define FBDUMP_CONTROL_RESIZE_FACTOR (4) + +void _mali_profiling_control(u32 action, u32 value); + +u32 _mali_profiling_get_l2_counters(_mali_profiling_l2_counter_values *values); + +int _mali_profiling_set_event(u32 counter_id, s32 event_id); + +u32 _mali_profiling_get_api_version(void); + +void _mali_profiling_get_mali_version(struct _mali_profiling_mali_version *values); + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_UTGARD_PROFILING_GATOR_API_H__ */ diff --git a/drivers/gpu/arm/mali/include/linux/mali/mali_utgard_uk_types.h b/drivers/gpu/arm/mali/include/linux/mali/mali_utgard_uk_types.h new file mode 100644 index 00000000000000..d032f9d58d6351 --- /dev/null +++ b/drivers/gpu/arm/mali/include/linux/mali/mali_utgard_uk_types.h @@ -0,0 +1,1132 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_uk_types.h + * Defines the types and constants used in the user-kernel interface + */ + +#ifndef __MALI_UTGARD_UK_TYPES_H__ +#define __MALI_UTGARD_UK_TYPES_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Iteration functions depend on these values being consecutive. */ +#define MALI_UK_TIMELINE_GP 0 +#define MALI_UK_TIMELINE_PP 1 +#define MALI_UK_TIMELINE_SOFT 2 +#define MALI_UK_TIMELINE_MAX 3 + +typedef struct { + u32 points[MALI_UK_TIMELINE_MAX]; + s32 sync_fd; +} _mali_uk_fence_t; + +/** + * @addtogroup uddapi Unified Device Driver (UDD) APIs + * + * @{ + */ + +/** + * @addtogroup u_k_api UDD User/Kernel Interface (U/K) APIs + * + * @{ + */ + +/** @defgroup _mali_uk_core U/K Core + * @{ */ + +/** Definition of subsystem numbers, to assist in creating a unique identifier + * for each U/K call. + * + * @see _mali_uk_functions */ +typedef enum { + _MALI_UK_CORE_SUBSYSTEM, /**< Core Group of U/K calls */ + _MALI_UK_MEMORY_SUBSYSTEM, /**< Memory Group of U/K calls */ + _MALI_UK_PP_SUBSYSTEM, /**< Fragment Processor Group of U/K calls */ + _MALI_UK_GP_SUBSYSTEM, /**< Vertex Processor Group of U/K calls */ + _MALI_UK_PROFILING_SUBSYSTEM, /**< Profiling Group of U/K calls */ + _MALI_UK_PMM_SUBSYSTEM, /**< Power Management Module Group of U/K calls */ + _MALI_UK_VSYNC_SUBSYSTEM, /**< VSYNC Group of U/K calls */ +} _mali_uk_subsystem_t; + +/** Within a function group each function has its unique sequence number + * to assist in creating a unique identifier for each U/K call. + * + * An ordered pair of numbers selected from + * ( \ref _mali_uk_subsystem_t,\ref _mali_uk_functions) will uniquely identify the + * U/K call across all groups of functions, and all functions. */ +typedef enum { + /** Core functions */ + + _MALI_UK_OPEN = 0, /**< _mali_ukk_open() */ + _MALI_UK_CLOSE, /**< _mali_ukk_close() */ + _MALI_UK_WAIT_FOR_NOTIFICATION, /**< _mali_ukk_wait_for_notification() */ + _MALI_UK_GET_API_VERSION, /**< _mali_ukk_get_api_version() */ + _MALI_UK_POST_NOTIFICATION, /**< _mali_ukk_post_notification() */ + _MALI_UK_GET_USER_SETTING, /**< _mali_ukk_get_user_setting() *//**< [out] */ + _MALI_UK_GET_USER_SETTINGS, /**< _mali_ukk_get_user_settings() *//**< [out] */ + _MALI_UK_REQUEST_HIGH_PRIORITY, /**< _mali_ukk_request_high_priority() */ + _MALI_UK_TIMELINE_GET_LATEST_POINT, /**< _mali_ukk_timeline_get_latest_point() */ + _MALI_UK_TIMELINE_WAIT, /**< _mali_ukk_timeline_wait() */ + _MALI_UK_TIMELINE_CREATE_SYNC_FENCE, /**< _mali_ukk_timeline_create_sync_fence() */ + _MALI_UK_SOFT_JOB_START, /**< _mali_ukk_soft_job_start() */ + _MALI_UK_SOFT_JOB_SIGNAL, /**< _mali_ukk_soft_job_signal() */ + + /** Memory functions */ + + _MALI_UK_INIT_MEM = 0, /**< _mali_ukk_init_mem() */ + _MALI_UK_TERM_MEM, /**< _mali_ukk_term_mem() */ + _MALI_UK_GET_BIG_BLOCK, /**< _mali_ukk_get_big_block() */ + _MALI_UK_FREE_BIG_BLOCK, /**< _mali_ukk_free_big_block() */ + _MALI_UK_MAP_MEM, /**< _mali_ukk_mem_mmap() */ + _MALI_UK_UNMAP_MEM, /**< _mali_ukk_mem_munmap() */ + _MALI_UK_QUERY_MMU_PAGE_TABLE_DUMP_SIZE, /**< _mali_ukk_mem_get_mmu_page_table_dump_size() */ + _MALI_UK_DUMP_MMU_PAGE_TABLE, /**< _mali_ukk_mem_dump_mmu_page_table() */ + _MALI_UK_ATTACH_DMA_BUF, /**< _mali_ukk_attach_dma_buf() */ + _MALI_UK_RELEASE_DMA_BUF, /**< _mali_ukk_release_dma_buf() */ + _MALI_UK_DMA_BUF_GET_SIZE, /**< _mali_ukk_dma_buf_get_size() */ + _MALI_UK_ATTACH_UMP_MEM, /**< _mali_ukk_attach_ump_mem() */ + _MALI_UK_RELEASE_UMP_MEM, /**< _mali_ukk_release_ump_mem() */ + _MALI_UK_MAP_EXT_MEM, /**< _mali_uku_map_external_mem() */ + _MALI_UK_UNMAP_EXT_MEM, /**< _mali_uku_unmap_external_mem() */ + _MALI_UK_VA_TO_MALI_PA, /**< _mali_uku_va_to_mali_pa() */ + _MALI_UK_MEM_WRITE_SAFE, /**< _mali_uku_mem_write_safe() */ + + /** Common functions for each core */ + + _MALI_UK_START_JOB = 0, /**< Start a Fragment/Vertex Processor Job on a core */ + _MALI_UK_GET_NUMBER_OF_CORES, /**< Get the number of Fragment/Vertex Processor cores */ + _MALI_UK_GET_CORE_VERSION, /**< Get the Fragment/Vertex Processor version compatible with all cores */ + + /** Fragment Processor Functions */ + + _MALI_UK_PP_START_JOB = _MALI_UK_START_JOB, /**< _mali_ukk_pp_start_job() */ + _MALI_UK_GET_PP_NUMBER_OF_CORES = _MALI_UK_GET_NUMBER_OF_CORES, /**< _mali_ukk_get_pp_number_of_cores() */ + _MALI_UK_GET_PP_CORE_VERSION = _MALI_UK_GET_CORE_VERSION, /**< _mali_ukk_get_pp_core_version() */ + _MALI_UK_PP_DISABLE_WB, /**< _mali_ukk_pp_job_disable_wb() */ + _MALI_UK_PP_AND_GP_START_JOB, /**< _mali_ukk_pp_and_gp_start_job() */ + + /** Vertex Processor Functions */ + + _MALI_UK_GP_START_JOB = _MALI_UK_START_JOB, /**< _mali_ukk_gp_start_job() */ + _MALI_UK_GET_GP_NUMBER_OF_CORES = _MALI_UK_GET_NUMBER_OF_CORES, /**< _mali_ukk_get_gp_number_of_cores() */ + _MALI_UK_GET_GP_CORE_VERSION = _MALI_UK_GET_CORE_VERSION, /**< _mali_ukk_get_gp_core_version() */ + _MALI_UK_GP_SUSPEND_RESPONSE, /**< _mali_ukk_gp_suspend_response() */ + + /** Profiling functions */ + + _MALI_UK_PROFILING_START = 0, /**< __mali_uku_profiling_start() */ + _MALI_UK_PROFILING_ADD_EVENT, /**< __mali_uku_profiling_add_event() */ + _MALI_UK_PROFILING_STOP, /**< __mali_uku_profiling_stop() */ + _MALI_UK_PROFILING_GET_EVENT, /**< __mali_uku_profiling_get_event() */ + _MALI_UK_PROFILING_CLEAR, /**< __mali_uku_profiling_clear() */ + _MALI_UK_PROFILING_GET_CONFIG, /**< __mali_uku_profiling_get_config() */ + _MALI_UK_PROFILING_REPORT_SW_COUNTERS,/**< __mali_uku_profiling_report_sw_counters() */ + + /** VSYNC reporting fuctions */ + _MALI_UK_VSYNC_EVENT_REPORT = 0, /**< _mali_ukk_vsync_event_report() */ + +} _mali_uk_functions; + +/** @brief Get the size necessary for system info + * + * @see _mali_ukk_get_system_info_size() + */ +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 size; /**< [out] size of buffer necessary to hold system information data, in bytes */ +} _mali_uk_get_system_info_size_s; + + +/** @defgroup _mali_uk_getsysteminfo U/K Get System Info + * @{ */ + +/** + * Type definition for the core version number. + * Used when returning the version number read from a core + * + * Its format is that of the 32-bit Version register for a particular core. + * Refer to the "Mali200 and MaliGP2 3D Graphics Processor Technical Reference + * Manual", ARM DDI 0415C, for more information. + */ +typedef u32 _mali_core_version; + +/** + * Enum values for the different modes the driver can be put in. + * Normal is the default mode. The driver then uses a job queue and takes job objects from the clients. + * Job completion is reported using the _mali_ukk_wait_for_notification call. + * The driver blocks this io command until a job has completed or failed or a timeout occurs. + * + * The 'raw' mode is reserved for future expansion. + */ +typedef enum _mali_driver_mode { + _MALI_DRIVER_MODE_RAW = 1, /**< Reserved for future expansion */ + _MALI_DRIVER_MODE_NORMAL = 2 /**< Normal mode of operation */ +} _mali_driver_mode; + +/** @brief List of possible cores + * + * add new entries to the end of this enum */ +typedef enum _mali_core_type { + _MALI_GP2 = 2, /**< MaliGP2 Programmable Vertex Processor */ + _MALI_200 = 5, /**< Mali200 Programmable Fragment Processor */ + _MALI_400_GP = 6, /**< Mali400 Programmable Vertex Processor */ + _MALI_400_PP = 7, /**< Mali400 Programmable Fragment Processor */ + /* insert new core here, do NOT alter the existing values */ +} _mali_core_type; + + +/** @brief Capabilities of Memory Banks + * + * These may be used to restrict memory banks for certain uses. They may be + * used when access is not possible (e.g. Bus does not support access to it) + * or when access is possible but not desired (e.g. Access is slow). + * + * In the case of 'possible but not desired', there is no way of specifying + * the flags as an optimization hint, so that the memory could be used as a + * last resort. + * + * @see _mali_mem_info + */ +typedef enum _mali_bus_usage { + + _MALI_PP_READABLE = (1<<0), /** Readable by the Fragment Processor */ + _MALI_PP_WRITEABLE = (1<<1), /** Writeable by the Fragment Processor */ + _MALI_GP_READABLE = (1<<2), /** Readable by the Vertex Processor */ + _MALI_GP_WRITEABLE = (1<<3), /** Writeable by the Vertex Processor */ + _MALI_CPU_READABLE = (1<<4), /** Readable by the CPU */ + _MALI_CPU_WRITEABLE = (1<<5), /** Writeable by the CPU */ + _MALI_GP_L2_ALLOC = (1<<6), /** GP allocate mali L2 cache lines*/ + _MALI_MMU_READABLE = _MALI_PP_READABLE | _MALI_GP_READABLE, /** Readable by the MMU (including all cores behind it) */ + _MALI_MMU_WRITEABLE = _MALI_PP_WRITEABLE | _MALI_GP_WRITEABLE, /** Writeable by the MMU (including all cores behind it) */ +} _mali_bus_usage; + +typedef enum mali_memory_cache_settings { + MALI_CACHE_STANDARD = 0, + MALI_CACHE_GP_READ_ALLOCATE = 1, +} mali_memory_cache_settings ; + + +/** @brief Information about the Mali Memory system + * + * Information is stored in a linked list, which is stored entirely in the + * buffer pointed to by the system_info member of the + * _mali_uk_get_system_info_s arguments provided to _mali_ukk_get_system_info() + * + * Each element of the linked list describes a single Mali Memory bank. + * Each allocation can only come from one bank, and will not cross multiple + * banks. + * + * On Mali-MMU systems, there is only one bank, which describes the maximum + * possible address range that could be allocated (which may be much less than + * the available physical memory) + * + * The flags member describes the capabilities of the memory. It is an error + * to attempt to build a job for a particular core (PP or GP) when the memory + * regions used do not have the capabilities for supporting that core. This + * would result in a job abort from the Device Driver. + * + * For example, it is correct to build a PP job where read-only data structures + * are taken from a memory with _MALI_PP_READABLE set and + * _MALI_PP_WRITEABLE clear, and a framebuffer with _MALI_PP_WRITEABLE set and + * _MALI_PP_READABLE clear. However, it would be incorrect to use a framebuffer + * where _MALI_PP_WRITEABLE is clear. + */ +typedef struct _mali_mem_info { + u32 size; /**< Size of the memory bank in bytes */ + _mali_bus_usage flags; /**< Capabilitiy flags of the memory */ + u32 maximum_order_supported; /**< log2 supported size */ + u32 identifier; /* mali_memory_cache_settings cache_settings; */ + struct _mali_mem_info * next; /**< Next List Link */ +} _mali_mem_info; + +/** @} */ /* end group _mali_uk_core */ + + +/** @defgroup _mali_uk_gp U/K Vertex Processor + * @{ */ + +/** @defgroup _mali_uk_gp_suspend_response_s Vertex Processor Suspend Response + * @{ */ + +/** @brief Arguments for _mali_ukk_gp_suspend_response() + * + * When _mali_wait_for_notification() receives notification that a + * Vertex Processor job was suspended, you need to send a response to indicate + * what needs to happen with this job. You can either abort or resume the job. + * + * - set @c code to indicate response code. This is either @c _MALIGP_JOB_ABORT or + * @c _MALIGP_JOB_RESUME_WITH_NEW_HEAP to indicate you will provide a new heap + * for the job that will resolve the out of memory condition for the job. + * - copy the @c cookie value from the @c _mali_uk_gp_job_suspended_s notification; + * this is an identifier for the suspended job + * - set @c arguments[0] and @c arguments[1] to zero if you abort the job. If + * you resume it, @c argument[0] should specify the Mali start address for the new + * heap and @c argument[1] the Mali end address of the heap. + * - pass in the user-kernel context @c ctx that was returned from _mali_ukk_open() + * + */ +typedef enum _maligp_job_suspended_response_code { + _MALIGP_JOB_ABORT, /**< Abort the Vertex Processor job */ + _MALIGP_JOB_RESUME_WITH_NEW_HEAP /**< Resume the Vertex Processor job with a new heap */ +} _maligp_job_suspended_response_code; + +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 cookie; /**< [in] cookie from the _mali_uk_gp_job_suspended_s notification */ + _maligp_job_suspended_response_code code; /**< [in] abort or resume response code, see \ref _maligp_job_suspended_response_code */ + u32 arguments[2]; /**< [in] 0 when aborting a job. When resuming a job, the Mali start and end address for a new heap to resume the job with */ +} _mali_uk_gp_suspend_response_s; + +/** @} */ /* end group _mali_uk_gp_suspend_response_s */ + +/** @defgroup _mali_uk_gpstartjob_s Vertex Processor Start Job + * @{ */ + +/** @brief Status indicating the result of starting a Vertex or Fragment processor job */ +typedef enum { + _MALI_UK_START_JOB_STARTED, /**< Job started */ + _MALI_UK_START_JOB_NOT_STARTED_DO_REQUEUE /**< Job could not be started at this time. Try starting the job again */ +} _mali_uk_start_job_status; + +/** @brief Status indicating the result of the execution of a Vertex or Fragment processor job */ + +typedef enum { + _MALI_UK_JOB_STATUS_END_SUCCESS = 1<<(16+0), + _MALI_UK_JOB_STATUS_END_OOM = 1<<(16+1), + _MALI_UK_JOB_STATUS_END_ABORT = 1<<(16+2), + _MALI_UK_JOB_STATUS_END_TIMEOUT_SW = 1<<(16+3), + _MALI_UK_JOB_STATUS_END_HANG = 1<<(16+4), + _MALI_UK_JOB_STATUS_END_SEG_FAULT = 1<<(16+5), + _MALI_UK_JOB_STATUS_END_ILLEGAL_JOB = 1<<(16+6), + _MALI_UK_JOB_STATUS_END_UNKNOWN_ERR = 1<<(16+7), + _MALI_UK_JOB_STATUS_END_SHUTDOWN = 1<<(16+8), + _MALI_UK_JOB_STATUS_END_SYSTEM_UNUSABLE = 1<<(16+9) +} _mali_uk_job_status; + +#define MALIGP2_NUM_REGS_FRAME (6) + +/** @brief Arguments for _mali_ukk_gp_start_job() + * + * To start a Vertex Processor job + * - associate the request with a reference to a @c mali_gp_job_info by setting + * user_job_ptr to the address of the @c mali_gp_job_info of the job. + * - set @c priority to the priority of the @c mali_gp_job_info + * - specify a timeout for the job by setting @c watchdog_msecs to the number of + * milliseconds the job is allowed to run. Specifying a value of 0 selects the + * default timeout in use by the device driver. + * - copy the frame registers from the @c mali_gp_job_info into @c frame_registers. + * - set the @c perf_counter_flag, @c perf_counter_src0 and @c perf_counter_src1 to zero + * for a non-instrumented build. For an instrumented build you can use up + * to two performance counters. Set the corresponding bit in @c perf_counter_flag + * to enable them. @c perf_counter_src0 and @c perf_counter_src1 specify + * the source of what needs to get counted (e.g. number of vertex loader + * cache hits). For source id values, see ARM DDI0415A, Table 3-60. + * - pass in the user-kernel context @c ctx that was returned from _mali_ukk_open() + * + * When @c _mali_ukk_gp_start_job() returns @c _MALI_OSK_ERR_OK, status contains the + * result of the request (see \ref _mali_uk_start_job_status). If the job could + * not get started (@c _MALI_UK_START_JOB_NOT_STARTED_DO_REQUEUE) it should be + * tried again. + * + * After the job has started, @c _mali_wait_for_notification() will be notified + * that the job finished or got suspended. It may get suspended due to + * resource shortage. If it finished (see _mali_ukk_wait_for_notification()) + * the notification will contain a @c _mali_uk_gp_job_finished_s result. If + * it got suspended the notification will contain a @c _mali_uk_gp_job_suspended_s + * result. + * + * The @c _mali_uk_gp_job_finished_s contains the job status (see \ref _mali_uk_job_status), + * the number of milliseconds the job took to render, and values of core registers + * when the job finished (irq status, performance counters, renderer list + * address). A job has finished succesfully when its status is + * @c _MALI_UK_JOB_STATUS_FINISHED. If the hardware detected a timeout while rendering + * the job, or software detected the job is taking more than watchdog_msecs to + * complete, the status will indicate @c _MALI_UK_JOB_STATUS_HANG. + * If the hardware detected a bus error while accessing memory associated with the + * job, status will indicate @c _MALI_UK_JOB_STATUS_SEG_FAULT. + * status will indicate @c _MALI_UK_JOB_STATUS_NOT_STARTED if the driver had to + * stop the job but the job didn't start on the hardware yet, e.g. when the + * driver shutdown. + * + * In case the job got suspended, @c _mali_uk_gp_job_suspended_s contains + * the @c user_job_ptr identifier used to start the job with, the @c reason + * why the job stalled (see \ref _maligp_job_suspended_reason) and a @c cookie + * to identify the core on which the job stalled. This @c cookie will be needed + * when responding to this nofication by means of _mali_ukk_gp_suspend_response(). + * (see _mali_ukk_gp_suspend_response()). The response is either to abort or + * resume the job. If the job got suspended due to an out of memory condition + * you may be able to resolve this by providing more memory and resuming the job. + * + */ +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 user_job_ptr; /**< [in] identifier for the job in user space, a @c mali_gp_job_info* */ + u32 priority; /**< [in] job priority. A lower number means higher priority */ + u32 frame_registers[MALIGP2_NUM_REGS_FRAME]; /**< [in] core specific registers associated with this job */ + u32 perf_counter_flag; /**< [in] bitmask indicating which performance counters to enable, see \ref _MALI_PERFORMANCE_COUNTER_FLAG_SRC0_ENABLE and related macro definitions */ + u32 perf_counter_src0; /**< [in] source id for performance counter 0 (see ARM DDI0415A, Table 3-60) */ + u32 perf_counter_src1; /**< [in] source id for performance counter 1 (see ARM DDI0415A, Table 3-60) */ + u32 frame_builder_id; /**< [in] id of the originating frame builder */ + u32 flush_id; /**< [in] flush id within the originating frame builder */ + _mali_uk_fence_t fence; /**< [in] fence this job must wait on */ + u32 *timeline_point_ptr; /**< [in,out] pointer to location where point on gp timeline for this job will be written */ +} _mali_uk_gp_start_job_s; + +#define _MALI_PERFORMANCE_COUNTER_FLAG_SRC0_ENABLE (1<<0) /**< Enable performance counter SRC0 for a job */ +#define _MALI_PERFORMANCE_COUNTER_FLAG_SRC1_ENABLE (1<<1) /**< Enable performance counter SRC1 for a job */ +#define _MALI_PERFORMANCE_COUNTER_FLAG_HEATMAP_ENABLE (1<<2) /**< Enable per tile (aka heatmap) generation with for a job (using the enabled counter sources) */ + +/** @} */ /* end group _mali_uk_gpstartjob_s */ + +typedef struct { + u32 user_job_ptr; /**< [out] identifier for the job in user space */ + _mali_uk_job_status status; /**< [out] status of finished job */ + u32 heap_current_addr; /**< [out] value of the GP PLB PL heap start address register */ + u32 perf_counter0; /**< [out] value of performance counter 0 (see ARM DDI0415A) */ + u32 perf_counter1; /**< [out] value of performance counter 1 (see ARM DDI0415A) */ +} _mali_uk_gp_job_finished_s; + +typedef struct { + u32 user_job_ptr; /**< [out] identifier for the job in user space */ + u32 cookie; /**< [out] identifier for the core in kernel space on which the job stalled */ +} _mali_uk_gp_job_suspended_s; + +/** @} */ /* end group _mali_uk_gp */ + + +/** @defgroup _mali_uk_pp U/K Fragment Processor + * @{ */ + +#define _MALI_PP_MAX_SUB_JOBS 8 + +#define _MALI_PP_MAX_FRAME_REGISTERS ((0x058/4)+1) + +#define _MALI_PP_MAX_WB_REGISTERS ((0x02C/4)+1) + +#define _MALI_DLBU_MAX_REGISTERS 4 + +/** Flag for _mali_uk_pp_start_job_s */ +#define _MALI_PP_JOB_FLAG_NO_NOTIFICATION (1<<0) +#define _MALI_PP_JOB_FLAG_IS_WINDOW_SURFACE (1<<1) + +/** @defgroup _mali_uk_ppstartjob_s Fragment Processor Start Job + * @{ */ + +/** @brief Arguments for _mali_ukk_pp_start_job() + * + * To start a Fragment Processor job + * - associate the request with a reference to a mali_pp_job by setting + * @c user_job_ptr to the address of the @c mali_pp_job of the job. + * - set @c priority to the priority of the mali_pp_job + * - specify a timeout for the job by setting @c watchdog_msecs to the number of + * milliseconds the job is allowed to run. Specifying a value of 0 selects the + * default timeout in use by the device driver. + * - copy the frame registers from the @c mali_pp_job into @c frame_registers. + * For MALI200 you also need to copy the write back 0,1 and 2 registers. + * - set the @c perf_counter_flag, @c perf_counter_src0 and @c perf_counter_src1 to zero + * for a non-instrumented build. For an instrumented build you can use up + * to two performance counters. Set the corresponding bit in @c perf_counter_flag + * to enable them. @c perf_counter_src0 and @c perf_counter_src1 specify + * the source of what needs to get counted (e.g. number of vertex loader + * cache hits). For source id values, see ARM DDI0415A, Table 3-60. + * - pass in the user-kernel context in @c ctx that was returned from _mali_ukk_open() + * + * When _mali_ukk_pp_start_job() returns @c _MALI_OSK_ERR_OK, @c status contains the + * result of the request (see \ref _mali_uk_start_job_status). If the job could + * not get started (@c _MALI_UK_START_JOB_NOT_STARTED_DO_REQUEUE) it should be + * tried again. + * + * After the job has started, _mali_wait_for_notification() will be notified + * when the job finished. The notification will contain a + * @c _mali_uk_pp_job_finished_s result. It contains the @c user_job_ptr + * identifier used to start the job with, the job @c status (see \ref _mali_uk_job_status), + * the number of milliseconds the job took to render, and values of core registers + * when the job finished (irq status, performance counters, renderer list + * address). A job has finished succesfully when its status is + * @c _MALI_UK_JOB_STATUS_FINISHED. If the hardware detected a timeout while rendering + * the job, or software detected the job is taking more than @c watchdog_msecs to + * complete, the status will indicate @c _MALI_UK_JOB_STATUS_HANG. + * If the hardware detected a bus error while accessing memory associated with the + * job, status will indicate @c _MALI_UK_JOB_STATUS_SEG_FAULT. + * status will indicate @c _MALI_UK_JOB_STATUS_NOT_STARTED if the driver had to + * stop the job but the job didn't start on the hardware yet, e.g. when the + * driver shutdown. + * + */ +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 user_job_ptr; /**< [in] identifier for the job in user space */ + u32 priority; /**< [in] job priority. A lower number means higher priority */ + u32 frame_registers[_MALI_PP_MAX_FRAME_REGISTERS]; /**< [in] core specific registers associated with first sub job, see ARM DDI0415A */ + u32 frame_registers_addr_frame[_MALI_PP_MAX_SUB_JOBS - 1]; /**< [in] ADDR_FRAME registers for sub job 1-7 */ + u32 frame_registers_addr_stack[_MALI_PP_MAX_SUB_JOBS - 1]; /**< [in] ADDR_STACK registers for sub job 1-7 */ + u32 wb0_registers[_MALI_PP_MAX_WB_REGISTERS]; + u32 wb1_registers[_MALI_PP_MAX_WB_REGISTERS]; + u32 wb2_registers[_MALI_PP_MAX_WB_REGISTERS]; + u32 dlbu_registers[_MALI_DLBU_MAX_REGISTERS]; /**< [in] Dynamic load balancing unit registers */ + u32 num_cores; /**< [in] Number of cores to set up (valid range: 1-4) */ + u32 perf_counter_flag; /**< [in] bitmask indicating which performance counters to enable, see \ref _MALI_PERFORMANCE_COUNTER_FLAG_SRC0_ENABLE and related macro definitions */ + u32 perf_counter_src0; /**< [in] source id for performance counter 0 (see ARM DDI0415A, Table 3-60) */ + u32 perf_counter_src1; /**< [in] source id for performance counter 1 (see ARM DDI0415A, Table 3-60) */ + u32 frame_builder_id; /**< [in] id of the originating frame builder */ + u32 flush_id; /**< [in] flush id within the originating frame builder */ + u32 flags; /**< [in] See _MALI_PP_JOB_FLAG_* for a list of avaiable flags */ + u32 tilesx; /**< [in] number of tiles in the x direction (needed for heatmap generation */ + u32 tilesy; /**< [in] number of tiles in y direction (needed for reading the heatmap memory) */ + u32 heatmap_mem; /**< [in] memory address to store counter values per tile (aka heatmap) */ + u32 num_memory_cookies; /**< [in] number of memory cookies attached to job */ + u32 *memory_cookies; /**< [in] memory cookies attached to job */ + _mali_uk_fence_t fence; /**< [in] fence this job must wait on */ + u32 *timeline_point_ptr; /**< [in,out] pointer to location where point on pp timeline for this job will be written */ +} _mali_uk_pp_start_job_s; + +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + _mali_uk_gp_start_job_s *gp_args; /**< [in,out] GP uk arguments (see _mali_uk_gp_start_job_s) */ + _mali_uk_pp_start_job_s *pp_args; /**< [in,out] PP uk arguments (see _mali_uk_pp_start_job_s) */ +} _mali_uk_pp_and_gp_start_job_s; + +/** @} */ /* end group _mali_uk_ppstartjob_s */ + +typedef struct { + u32 user_job_ptr; /**< [out] identifier for the job in user space */ + _mali_uk_job_status status; /**< [out] status of finished job */ + u32 perf_counter0[_MALI_PP_MAX_SUB_JOBS]; /**< [out] value of perfomance counter 0 (see ARM DDI0415A), one for each sub job */ + u32 perf_counter1[_MALI_PP_MAX_SUB_JOBS]; /**< [out] value of perfomance counter 1 (see ARM DDI0415A), one for each sub job */ + u32 perf_counter_src0; + u32 perf_counter_src1; +} _mali_uk_pp_job_finished_s; + +typedef struct { + u32 number_of_enabled_cores; /**< [out] the new number of enabled cores */ +} _mali_uk_pp_num_cores_changed_s; + + + +/** + * Flags to indicate write-back units + */ +typedef enum { + _MALI_UK_PP_JOB_WB0 = 1, + _MALI_UK_PP_JOB_WB1 = 2, + _MALI_UK_PP_JOB_WB2 = 4, +} _mali_uk_pp_job_wbx_flag; + +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 fb_id; /**< [in] Frame builder ID of job to disable WB units for */ + u32 wb0_memory; + u32 wb1_memory; + u32 wb2_memory; +} _mali_uk_pp_disable_wb_s; + + +/** @} */ /* end group _mali_uk_pp */ + +/** @defgroup _mali_uk_soft_job U/K Soft Job + * @{ */ + +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 type; /**< [in] type of soft job */ + u32 user_job; /**< [in] identifier for the job in user space */ + u32 *job_id_ptr; /**< [in,out] pointer to location where job id will be written */ + _mali_uk_fence_t fence; /**< [in] fence this job must wait on */ + u32 point; /**< [out] point on soft timeline for this job */ +} _mali_uk_soft_job_start_s; + +typedef struct { + u32 user_job; /**< [out] identifier for the job in user space */ +} _mali_uk_soft_job_activated_s; + +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 job_id; /**< [in] id for soft job */ +} _mali_uk_soft_job_signal_s; + +/** @} */ /* end group _mali_uk_soft_job */ + +/** @addtogroup _mali_uk_core U/K Core + * @{ */ + +/** @defgroup _mali_uk_waitfornotification_s Wait For Notification + * @{ */ + +/** @brief Notification type encodings + * + * Each Notification type is an ordered pair of (subsystem,id), and is unique. + * + * The encoding of subsystem,id into a 32-bit word is: + * encoding = (( subsystem << _MALI_NOTIFICATION_SUBSYSTEM_SHIFT ) & _MALI_NOTIFICATION_SUBSYSTEM_MASK) + * | (( id << _MALI_NOTIFICATION_ID_SHIFT ) & _MALI_NOTIFICATION_ID_MASK) + * + * @see _mali_uk_wait_for_notification_s + */ +typedef enum { + /** core notifications */ + + _MALI_NOTIFICATION_CORE_SHUTDOWN_IN_PROGRESS = (_MALI_UK_CORE_SUBSYSTEM << 16) | 0x20, + _MALI_NOTIFICATION_APPLICATION_QUIT = (_MALI_UK_CORE_SUBSYSTEM << 16) | 0x40, + _MALI_NOTIFICATION_SETTINGS_CHANGED = (_MALI_UK_CORE_SUBSYSTEM << 16) | 0x80, + _MALI_NOTIFICATION_SOFT_ACTIVATED = (_MALI_UK_CORE_SUBSYSTEM << 16) | 0x100, + + /** Fragment Processor notifications */ + + _MALI_NOTIFICATION_PP_FINISHED = (_MALI_UK_PP_SUBSYSTEM << 16) | 0x10, + _MALI_NOTIFICATION_PP_NUM_CORE_CHANGE = (_MALI_UK_PP_SUBSYSTEM << 16) | 0x20, + + /** Vertex Processor notifications */ + + _MALI_NOTIFICATION_GP_FINISHED = (_MALI_UK_GP_SUBSYSTEM << 16) | 0x10, + _MALI_NOTIFICATION_GP_STALLED = (_MALI_UK_GP_SUBSYSTEM << 16) | 0x20, + +} _mali_uk_notification_type; + +/** to assist in splitting up 32-bit notification value in subsystem and id value */ +#define _MALI_NOTIFICATION_SUBSYSTEM_MASK 0xFFFF0000 +#define _MALI_NOTIFICATION_SUBSYSTEM_SHIFT 16 +#define _MALI_NOTIFICATION_ID_MASK 0x0000FFFF +#define _MALI_NOTIFICATION_ID_SHIFT 0 + + +/** @brief Enumeration of possible settings which match mali_setting_t in user space + * + * + */ +typedef enum { + _MALI_UK_USER_SETTING_SW_EVENTS_ENABLE = 0, + _MALI_UK_USER_SETTING_COLORBUFFER_CAPTURE_ENABLED, + _MALI_UK_USER_SETTING_DEPTHBUFFER_CAPTURE_ENABLED, + _MALI_UK_USER_SETTING_STENCILBUFFER_CAPTURE_ENABLED, + _MALI_UK_USER_SETTING_PER_TILE_COUNTERS_CAPTURE_ENABLED, + _MALI_UK_USER_SETTING_BUFFER_CAPTURE_COMPOSITOR, + _MALI_UK_USER_SETTING_BUFFER_CAPTURE_WINDOW, + _MALI_UK_USER_SETTING_BUFFER_CAPTURE_OTHER, + _MALI_UK_USER_SETTING_BUFFER_CAPTURE_N_FRAMES, + _MALI_UK_USER_SETTING_BUFFER_CAPTURE_RESIZE_FACTOR, + _MALI_UK_USER_SETTING_SW_COUNTER_ENABLED, + _MALI_UK_USER_SETTING_MAX, +} _mali_uk_user_setting_t; + +/* See mali_user_settings_db.c */ +extern const char *_mali_uk_user_setting_descriptions[]; +#define _MALI_UK_USER_SETTING_DESCRIPTIONS \ +{ \ + "sw_events_enable", \ + "colorbuffer_capture_enable", \ + "depthbuffer_capture_enable", \ + "stencilbuffer_capture_enable", \ + "per_tile_counters_enable", \ + "buffer_capture_compositor", \ + "buffer_capture_window", \ + "buffer_capture_other", \ + "buffer_capture_n_frames", \ + "buffer_capture_resize_factor", \ + "sw_counters_enable", \ +}; + +/** @brief struct to hold the value to a particular setting as seen in the kernel space + */ +typedef struct { + _mali_uk_user_setting_t setting; + u32 value; +} _mali_uk_settings_changed_s; + +/** @brief Arguments for _mali_ukk_wait_for_notification() + * + * On successful return from _mali_ukk_wait_for_notification(), the members of + * this structure will indicate the reason for notification. + * + * Specifically, the source of the notification can be identified by the + * subsystem and id fields of the mali_uk_notification_type in the code.type + * member. The type member is encoded in a way to divide up the types into a + * subsystem field, and a per-subsystem ID field. See + * _mali_uk_notification_type for more information. + * + * Interpreting the data union member depends on the notification type: + * + * - type == _MALI_NOTIFICATION_CORE_SHUTDOWN_IN_PROGRESS + * - The kernel side is shutting down. No further + * _mali_uk_wait_for_notification() calls should be made. + * - In this case, the value of the data union member is undefined. + * - This is used to indicate to the user space client that it should close + * the connection to the Mali Device Driver. + * - type == _MALI_NOTIFICATION_PP_FINISHED + * - The notification data is of type _mali_uk_pp_job_finished_s. It contains the user_job_ptr + * identifier used to start the job with, the job status, the number of milliseconds the job took to render, + * and values of core registers when the job finished (irq status, performance counters, renderer list + * address). + * - A job has finished succesfully when its status member is _MALI_UK_JOB_STATUS_FINISHED. + * - If the hardware detected a timeout while rendering the job, or software detected the job is + * taking more than watchdog_msecs (see _mali_ukk_pp_start_job()) to complete, the status member will + * indicate _MALI_UK_JOB_STATUS_HANG. + * - If the hardware detected a bus error while accessing memory associated with the job, status will + * indicate _MALI_UK_JOB_STATUS_SEG_FAULT. + * - Status will indicate MALI_UK_JOB_STATUS_NOT_STARTED if the driver had to stop the job but the job + * didn't start the hardware yet, e.g. when the driver closes. + * - type == _MALI_NOTIFICATION_GP_FINISHED + * - The notification data is of type _mali_uk_gp_job_finished_s. The notification is similar to that of + * type == _MALI_NOTIFICATION_PP_FINISHED, except that several other GP core register values are returned. + * The status values have the same meaning for type == _MALI_NOTIFICATION_PP_FINISHED. + * - type == _MALI_NOTIFICATION_GP_STALLED + * - The nofication data is of type _mali_uk_gp_job_suspended_s. It contains the user_job_ptr + * identifier used to start the job with, the reason why the job stalled and a cookie to identify the core on + * which the job stalled. + * - The reason member of gp_job_suspended is set to _MALIGP_JOB_SUSPENDED_OUT_OF_MEMORY + * when the polygon list builder unit has run out of memory. + */ +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + _mali_uk_notification_type type; /**< [out] Type of notification available */ + union { + _mali_uk_gp_job_suspended_s gp_job_suspended;/**< [out] Notification data for _MALI_NOTIFICATION_GP_STALLED notification type */ + _mali_uk_gp_job_finished_s gp_job_finished; /**< [out] Notification data for _MALI_NOTIFICATION_GP_FINISHED notification type */ + _mali_uk_pp_job_finished_s pp_job_finished; /**< [out] Notification data for _MALI_NOTIFICATION_PP_FINISHED notification type */ + _mali_uk_settings_changed_s setting_changed;/**< [out] Notification data for _MALI_NOTIFICAATION_SETTINGS_CHANGED notification type */ + _mali_uk_soft_job_activated_s soft_job_activated; /**< [out] Notification data for _MALI_NOTIFICATION_SOFT_ACTIVATED notification type */ + } data; +} _mali_uk_wait_for_notification_s; + +/** @brief Arguments for _mali_ukk_post_notification() + * + * Posts the specified notification to the notification queue for this application. + * This is used to send a quit message to the callback thread. + */ +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + _mali_uk_notification_type type; /**< [in] Type of notification to post */ +} _mali_uk_post_notification_s; + +/** @} */ /* end group _mali_uk_waitfornotification_s */ + +/** @defgroup _mali_uk_getapiversion_s Get API Version + * @{ */ + +/** helpers for Device Driver API version handling */ + +/** @brief Encode a version ID from a 16-bit input + * + * @note the input is assumed to be 16 bits. It must not exceed 16 bits. */ +#define _MAKE_VERSION_ID(x) (((x) << 16UL) | (x)) + +/** @brief Check whether a 32-bit value is likely to be Device Driver API + * version ID. */ +#define _IS_VERSION_ID(x) (((x) & 0xFFFF) == (((x) >> 16UL) & 0xFFFF)) + +/** @brief Decode a 16-bit version number from a 32-bit Device Driver API version + * ID */ +#define _GET_VERSION(x) (((x) >> 16UL) & 0xFFFF) + +/** @brief Determine whether two 32-bit encoded version IDs match */ +#define _IS_API_MATCH(x, y) (IS_VERSION_ID((x)) && IS_VERSION_ID((y)) && (GET_VERSION((x)) == GET_VERSION((y)))) + +/** + * API version define. + * Indicates the version of the kernel API + * The version is a 16bit integer incremented on each API change. + * The 16bit integer is stored twice in a 32bit integer + * For example, for version 1 the value would be 0x00010001 + */ +#define _MALI_API_VERSION 401 +#define _MALI_UK_API_VERSION _MAKE_VERSION_ID(_MALI_API_VERSION) + +/** + * The API version is a 16-bit integer stored in both the lower and upper 16-bits + * of a 32-bit value. The 16-bit API version value is incremented on each API + * change. Version 1 would be 0x00010001. Used in _mali_uk_get_api_version_s. + */ +typedef u32 _mali_uk_api_version; + +/** @brief Arguments for _mali_uk_get_api_version() + * + * The user-side interface version must be written into the version member, + * encoded using _MAKE_VERSION_ID(). It will be compared to the API version of + * the kernel-side interface. + * + * On successful return, the version member will be the API version of the + * kernel-side interface. _MALI_UK_API_VERSION macro defines the current version + * of the API. + * + * The compatible member must be checked to see if the version of the user-side + * interface is compatible with the kernel-side interface, since future versions + * of the interface may be backwards compatible. + */ +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + _mali_uk_api_version version; /**< [in,out] API version of user-side interface. */ + int compatible; /**< [out] @c 1 when @version is compatible, @c 0 otherwise */ +} _mali_uk_get_api_version_s; +/** @} */ /* end group _mali_uk_getapiversion_s */ + +/** @defgroup _mali_uk_get_user_settings_s Get user space settings */ + +/** @brief struct to keep the matching values of the user space settings within certain context + * + * Each member of the settings array corresponds to a matching setting in the user space and its value is the value + * of that particular setting. + * + * All settings are given reference to the context pointed to by the ctx pointer. + * + */ +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 settings[_MALI_UK_USER_SETTING_MAX]; /**< [out] The values for all settings */ +} _mali_uk_get_user_settings_s; + +/** @brief struct to hold the value of a particular setting from the user space within a given context + */ +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + _mali_uk_user_setting_t setting; /**< [in] setting to get */ + u32 value; /**< [out] value of setting */ +} _mali_uk_get_user_setting_s; + +/** @brief Arguments for _mali_ukk_request_high_priority() */ +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ +} _mali_uk_request_high_priority_s; + +/** @} */ /* end group _mali_uk_core */ + + +/** @defgroup _mali_uk_memory U/K Memory + * @{ */ + +/** Flag for _mali_uk_map_external_mem_s, _mali_uk_attach_ump_mem_s and _mali_uk_attach_dma_buf_s */ +#define _MALI_MAP_EXTERNAL_MAP_GUARD_PAGE (1<<0) + +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 phys_addr; /**< [in] physical address */ + u32 size; /**< [in] size */ + u32 mali_address; /**< [in] mali address to map the physical memory to */ + u32 rights; /**< [in] rights necessary for accessing memory */ + u32 flags; /**< [in] flags, see \ref _MALI_MAP_EXTERNAL_MAP_GUARD_PAGE */ + u32 cookie; /**< [out] identifier for mapped memory object in kernel space */ +} _mali_uk_map_external_mem_s; + +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 cookie; /**< [out] identifier for mapped memory object in kernel space */ +} _mali_uk_unmap_external_mem_s; + +/** @note This is identical to _mali_uk_map_external_mem_s above, however phys_addr is replaced by memory descriptor */ +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 mem_fd; /**< [in] Memory descriptor */ + u32 size; /**< [in] size */ + u32 mali_address; /**< [in] mali address to map the physical memory to */ + u32 rights; /**< [in] rights necessary for accessing memory */ + u32 flags; /**< [in] flags, see \ref _MALI_MAP_EXTERNAL_MAP_GUARD_PAGE */ + u32 cookie; /**< [out] identifier for mapped memory object in kernel space */ +} _mali_uk_attach_dma_buf_s; + +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 mem_fd; /**< [in] Memory descriptor */ + u32 size; /**< [out] size */ +} _mali_uk_dma_buf_get_size_s; + +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 cookie; /**< [in] identifier for mapped memory object in kernel space */ +} _mali_uk_release_dma_buf_s; + +/** @note This is identical to _mali_uk_map_external_mem_s above, however phys_addr is replaced by secure_id */ +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 secure_id; /**< [in] secure id */ + u32 size; /**< [in] size */ + u32 mali_address; /**< [in] mali address to map the physical memory to */ + u32 rights; /**< [in] rights necessary for accessing memory */ + u32 flags; /**< [in] flags, see \ref _MALI_MAP_EXTERNAL_MAP_GUARD_PAGE */ + u32 cookie; /**< [out] identifier for mapped memory object in kernel space */ +} _mali_uk_attach_ump_mem_s; + +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 cookie; /**< [in] identifier for mapped memory object in kernel space */ +} _mali_uk_release_ump_mem_s; + +/** @brief Arguments for _mali_ukk_va_to_mali_pa() + * + * if size is zero or not a multiple of the system's page size, it will be + * rounded up to the next multiple of the page size. This will occur before + * any other use of the size parameter. + * + * if va is not PAGE_SIZE aligned, it will be rounded down to the next page + * boundary. + * + * The range (va) to ((u32)va)+(size-1) inclusive will be checked for physical + * contiguity. + * + * The implementor will check that the entire physical range is allowed to be mapped + * into user-space. + * + * Failure will occur if either of the above are not satisfied. + * + * Otherwise, the physical base address of the range is returned through pa, + * va is updated to be page aligned, and size is updated to be a non-zero + * multiple of the system's pagesize. + */ +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + void *va; /**< [in,out] Virtual address of the start of the range */ + u32 pa; /**< [out] Physical base address of the range */ + u32 size; /**< [in,out] Size of the range, in bytes. */ +} _mali_uk_va_to_mali_pa_s; + +/** + * @brief Arguments for _mali_uk[uk]_mem_write_safe() + */ +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + const void *src; /**< [in] Pointer to source data */ + void *dest; /**< [in] Destination Mali buffer */ + u32 size; /**< [in,out] Number of bytes to write/copy on input, number of bytes actually written/copied on output */ +} _mali_uk_mem_write_safe_s; + +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 size; /**< [out] size of MMU page table information (registers + page tables) */ +} _mali_uk_query_mmu_page_table_dump_size_s; + +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 size; /**< [in] size of buffer to receive mmu page table information */ + void *buffer; /**< [in,out] buffer to receive mmu page table information */ + u32 register_writes_size; /**< [out] size of MMU register dump */ + u32 *register_writes; /**< [out] pointer within buffer where MMU register dump is stored */ + u32 page_table_dump_size; /**< [out] size of MMU page table dump */ + u32 *page_table_dump; /**< [out] pointer within buffer where MMU page table dump is stored */ +} _mali_uk_dump_mmu_page_table_s; + +/** @} */ /* end group _mali_uk_memory */ + + +/** @addtogroup _mali_uk_pp U/K Fragment Processor + * @{ */ + +/** @brief Arguments for _mali_ukk_get_pp_number_of_cores() + * + * - pass in the user-kernel context @c ctx that was returned from _mali_ukk_open() + * - Upon successful return from _mali_ukk_get_pp_number_of_cores(), @c number_of_cores + * will contain the number of Fragment Processor cores in the system. + */ +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 number_of_total_cores; /**< [out] Total number of Fragment Processor cores in the system */ + u32 number_of_enabled_cores; /**< [out] Number of enabled Fragment Processor cores */ +} _mali_uk_get_pp_number_of_cores_s; + +/** @brief Arguments for _mali_ukk_get_pp_core_version() + * + * - pass in the user-kernel context @c ctx that was returned from _mali_ukk_open() + * - Upon successful return from _mali_ukk_get_pp_core_version(), @c version contains + * the version that all Fragment Processor cores are compatible with. + */ +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + _mali_core_version version; /**< [out] version returned from core, see \ref _mali_core_version */ +} _mali_uk_get_pp_core_version_s; + +/** @} */ /* end group _mali_uk_pp */ + + +/** @addtogroup _mali_uk_gp U/K Vertex Processor + * @{ */ + +/** @brief Arguments for _mali_ukk_get_gp_number_of_cores() + * + * - pass in the user-kernel context @c ctx that was returned from _mali_ukk_open() + * - Upon successful return from _mali_ukk_get_gp_number_of_cores(), @c number_of_cores + * will contain the number of Vertex Processor cores in the system. + */ +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 number_of_cores; /**< [out] number of Vertex Processor cores in the system */ +} _mali_uk_get_gp_number_of_cores_s; + +/** @brief Arguments for _mali_ukk_get_gp_core_version() + * + * - pass in the user-kernel context @c ctx that was returned from _mali_ukk_open() + * - Upon successful return from _mali_ukk_get_gp_core_version(), @c version contains + * the version that all Vertex Processor cores are compatible with. + */ +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + _mali_core_version version; /**< [out] version returned from core, see \ref _mali_core_version */ +} _mali_uk_get_gp_core_version_s; + +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 limit; /**< [in,out] The desired limit for number of events to record on input, actual limit on output */ +} _mali_uk_profiling_start_s; + +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 event_id; /**< [in] event id to register (see enum mali_profiling_events for values) */ + u32 data[5]; /**< [in] event specific data */ +} _mali_uk_profiling_add_event_s; + +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 count; /**< [out] The number of events sampled */ +} _mali_uk_profiling_stop_s; + +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 index; /**< [in] which index to get (starting at zero) */ + u64 timestamp; /**< [out] timestamp of event */ + u32 event_id; /**< [out] event id of event (see enum mali_profiling_events for values) */ + u32 data[5]; /**< [out] event specific data */ +} _mali_uk_profiling_get_event_s; + +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ +} _mali_uk_profiling_clear_s; + +/** @} */ /* end group _mali_uk_gp */ + + +/** @addtogroup _mali_uk_memory U/K Memory + * @{ */ + +/** @brief Arguments to _mali_ukk_mem_mmap() + * + * Use of the phys_addr member depends on whether the driver is compiled for + * Mali-MMU or nonMMU: + * - in the nonMMU case, this is the physical address of the memory as seen by + * the CPU (which may be a constant offset from that used by Mali) + * - in the MMU case, this is the Mali Virtual base address of the memory to + * allocate, and the particular physical pages used to back the memory are + * entirely determined by _mali_ukk_mem_mmap(). The details of the physical pages + * are not reported to user-space for security reasons. + * + * The cookie member must be stored for use later when freeing the memory by + * calling _mali_ukk_mem_munmap(). In the Mali-MMU case, the cookie is secure. + * + * The ukk_private word must be set to zero when calling from user-space. On + * Kernel-side, the OS implementation of the U/K interface can use it to + * communicate data to the OS implementation of the OSK layer. In particular, + * _mali_ukk_get_big_block() directly calls _mali_ukk_mem_mmap directly, and + * will communicate its own ukk_private word through the ukk_private member + * here. The common code itself will not inspect or modify the ukk_private + * word, and so it may be safely used for whatever purposes necessary to + * integrate Mali Memory handling into the OS. + * + * The uku_private member is currently reserved for use by the user-side + * implementation of the U/K interface. Its value must be zero. + */ +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + void *mapping; /**< [out] Returns user-space virtual address for the mapping */ + u32 size; /**< [in] Size of the requested mapping */ + u32 phys_addr; /**< [in] Physical address - could be offset, depending on caller+callee convention */ + u32 cookie; /**< [out] Returns a cookie for use in munmap calls */ + void *uku_private; /**< [in] User-side Private word used by U/K interface */ + void *ukk_private; /**< [in] Kernel-side Private word used by U/K interface */ + mali_memory_cache_settings cache_settings; /**< [in] Option to set special cache flags, tuning L2 efficency */ +} _mali_uk_mem_mmap_s; + +/** @brief Arguments to _mali_ukk_mem_munmap() + * + * The cookie and mapping members must be that returned from the same previous + * call to _mali_ukk_mem_mmap(). The size member must correspond to cookie + * and mapping - that is, it must be the value originally supplied to a call to + * _mali_ukk_mem_mmap that returned the values of mapping and cookie. + * + * An error will be returned if an attempt is made to unmap only part of the + * originally obtained range, or to unmap more than was originally obtained. + */ +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + void *mapping; /**< [in] The mapping returned from mmap call */ + u32 size; /**< [in] The size passed to mmap call */ + u32 cookie; /**< [in] Cookie from mmap call */ +} _mali_uk_mem_munmap_s; +/** @} */ /* end group _mali_uk_memory */ + +/** @defgroup _mali_uk_vsync U/K VSYNC Wait Reporting Module + * @{ */ + +/** @brief VSYNC events + * + * These events are reported when DDK starts to wait for vsync and when the + * vsync has occured and the DDK can continue on the next frame. + */ +typedef enum _mali_uk_vsync_event { + _MALI_UK_VSYNC_EVENT_BEGIN_WAIT = 0, + _MALI_UK_VSYNC_EVENT_END_WAIT +} _mali_uk_vsync_event; + +/** @brief Arguments to _mali_ukk_vsync_event() + * + */ +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + _mali_uk_vsync_event event; /**< [in] VSYNCH event type */ +} _mali_uk_vsync_event_report_s; + +/** @} */ /* end group _mali_uk_vsync */ + +/** @defgroup _mali_uk_sw_counters_report U/K Software Counter Reporting + * @{ */ + +/** @brief Software counter values + * + * Values recorded for each of the software counters during a single renderpass. + */ +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32* counters; /**< [in] The array of counter values */ + u32 num_counters; /**< [in] The number of elements in counters array */ +} _mali_uk_sw_counters_report_s; + +/** @} */ /* end group _mali_uk_sw_counters_report */ + +/** @defgroup _mali_uk_timeline U/K Mali Timeline + * @{ */ + +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 timeline; /**< [in] timeline id */ + u32 point; /**< [out] latest point on timeline */ +} _mali_uk_timeline_get_latest_point_s; + +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + _mali_uk_fence_t fence; /**< [in] fence */ + u32 timeout; /**< [in] timeout (0 for no wait, -1 for blocking) */ + u32 status; /**< [out] status of fence (1 if signaled, 0 if timeout) */ +} _mali_uk_timeline_wait_s; + +typedef struct { + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + _mali_uk_fence_t fence; /**< [in] mali fence to create linux sync fence from */ + s32 sync_fd; /**< [out] file descriptor for new linux sync fence */ +} _mali_uk_timeline_create_sync_fence_s; + +/** @} */ /* end group _mali_uk_timeline */ + +/** @} */ /* end group u_k_api */ + +/** @} */ /* end group uddapi */ + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_UTGARD_UK_TYPES_H__ */ diff --git a/drivers/gpu/arm/mali/linux/license/gpl/mali_kernel_license.h b/drivers/gpu/arm/mali/linux/license/gpl/mali_kernel_license.h new file mode 100644 index 00000000000000..009acc4c3ff946 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/license/gpl/mali_kernel_license.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2010, 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_kernel_license.h + * Defines for the macro MODULE_LICENSE. + */ + +#ifndef __MALI_KERNEL_LICENSE_H__ +#define __MALI_KERNEL_LICENSE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define MALI_KERNEL_LINUX_LICENSE "GPL" +#define MALI_LICENSE_IS_GPL 1 + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_KERNEL_LICENSE_H__ */ diff --git a/drivers/gpu/arm/mali/linux/mali_device_pause_resume.c b/drivers/gpu/arm/mali/linux/mali_device_pause_resume.c new file mode 100644 index 00000000000000..440602e44932a5 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_device_pause_resume.c @@ -0,0 +1,38 @@ +/** + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_device_pause_resume.c + * Implementation of the Mali pause/resume functionality + */ + +#include +#include +#include "mali_gp_scheduler.h" +#include "mali_pp_scheduler.h" + +void mali_dev_pause(void) +{ + mali_gp_scheduler_suspend(); + mali_pp_scheduler_suspend(); + mali_group_power_off(MALI_FALSE); + mali_l2_cache_pause_all(MALI_TRUE); +} + +EXPORT_SYMBOL(mali_dev_pause); + +void mali_dev_resume(void) +{ + mali_l2_cache_pause_all(MALI_FALSE); + mali_gp_scheduler_resume(); + mali_pp_scheduler_resume(); +} + +EXPORT_SYMBOL(mali_dev_resume); diff --git a/drivers/gpu/arm/mali/linux/mali_kernel_linux.c b/drivers/gpu/arm/mali/linux/mali_kernel_linux.c new file mode 100644 index 00000000000000..186b4dcddf45aa --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_kernel_linux.c @@ -0,0 +1,804 @@ +/** + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_kernel_linux.c + * Implementation of the Linux device driver entrypoints + */ +#include /* kernel module definitions */ +#include /* file system operations */ +#include /* character device definitions */ +#include /* memory manager definitions */ +#include +#include +#include +#include "mali_kernel_license.h" +#include +#include +#include +#include "mali_kernel_common.h" +#include "mali_session.h" +#include "mali_kernel_core.h" +#include "mali_osk.h" +#include "mali_kernel_linux.h" +#include "mali_ukk.h" +#include "mali_ukk_wrappers.h" +#include "mali_kernel_sysfs.h" +#include "mali_pm.h" +#include "mali_kernel_license.h" +#include "mali_memory.h" +#include "mali_memory_dma_buf.h" +#if defined(CONFIG_MALI400_INTERNAL_PROFILING) +#include "mali_profiling_internal.h" +#endif + +/* Streamline support for the Mali driver */ +#if defined(CONFIG_TRACEPOINTS) && defined(CONFIG_MALI400_PROFILING) +/* Ask Linux to create the tracepoints */ +#define CREATE_TRACE_POINTS +#include "mali_linux_trace.h" +#endif /* CONFIG_TRACEPOINTS */ + +/* from the __malidrv_build_info.c file that is generated during build */ +extern const char *__malidrv_build_info(void); + +/* Module parameter to control log level */ +int mali_debug_level = 2; +module_param(mali_debug_level, int, S_IRUSR | S_IWUSR | S_IWGRP | S_IRGRP | S_IROTH); /* rw-rw-r-- */ +MODULE_PARM_DESC(mali_debug_level, "Higher number, more dmesg output"); + +extern int mali_max_job_runtime; +module_param(mali_max_job_runtime, int, S_IRUSR | S_IWUSR | S_IWGRP | S_IRGRP | S_IROTH); +MODULE_PARM_DESC(mali_max_job_runtime, "Maximum allowed job runtime in msecs.\nJobs will be killed after this no matter what"); + +extern int mali_l2_max_reads; +module_param(mali_l2_max_reads, int, S_IRUSR | S_IRGRP | S_IROTH); +MODULE_PARM_DESC(mali_l2_max_reads, "Maximum reads for Mali L2 cache"); + +extern unsigned int mali_dedicated_mem_start; +module_param(mali_dedicated_mem_start, uint, S_IRUSR | S_IRGRP | S_IROTH); +MODULE_PARM_DESC(mali_dedicated_mem_start, "Physical start address of dedicated Mali GPU memory."); + +extern unsigned int mali_dedicated_mem_size; +module_param(mali_dedicated_mem_size, uint, S_IRUSR | S_IRGRP | S_IROTH); +MODULE_PARM_DESC(mali_dedicated_mem_size, "Size of dedicated Mali GPU memory."); + +extern unsigned int mali_shared_mem_size; +module_param(mali_shared_mem_size, uint, S_IRUSR | S_IRGRP | S_IROTH); +MODULE_PARM_DESC(mali_shared_mem_size, "Size of shared Mali GPU memory."); + +#if defined(CONFIG_MALI400_PROFILING) +extern int mali_boot_profiling; +module_param(mali_boot_profiling, int, S_IRUSR | S_IRGRP | S_IROTH); +MODULE_PARM_DESC(mali_boot_profiling, "Start profiling as a part of Mali driver initialization"); +#endif + +extern int mali_max_pp_cores_group_1; +module_param(mali_max_pp_cores_group_1, int, S_IRUSR | S_IRGRP | S_IROTH); +MODULE_PARM_DESC(mali_max_pp_cores_group_1, "Limit the number of PP cores to use from first PP group."); + +extern int mali_max_pp_cores_group_2; +module_param(mali_max_pp_cores_group_2, int, S_IRUSR | S_IRGRP | S_IROTH); +MODULE_PARM_DESC(mali_max_pp_cores_group_2, "Limit the number of PP cores to use from second PP group (Mali-450 only)."); + +#if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY) +/** the max fps the same as display vsync default 60, can set by module insert parameter */ +extern int mali_max_system_fps; +module_param(mali_max_system_fps, int, S_IRUSR | S_IWUSR | S_IWGRP | S_IRGRP | S_IROTH); +MODULE_PARM_DESC(mali_max_system_fps, "Max system fps the same as display VSYNC."); + +/** a lower limit on their desired FPS default 58, can set by module insert parameter*/ +extern int mali_desired_fps; +module_param(mali_desired_fps, int, S_IRUSR | S_IWUSR | S_IWGRP | S_IRGRP | S_IROTH); +MODULE_PARM_DESC(mali_desired_fps, "A bit lower than max_system_fps which user desired fps"); +#endif + +#if MALI_ENABLE_CPU_CYCLES +#include +#include +#include +static struct timer_list mali_init_cpu_clock_timers[8]; +static u32 mali_cpu_clock_last_value[8] = {0,}; +#endif + +/* Export symbols from common code: mali_user_settings.c */ +#include "mali_user_settings_db.h" +EXPORT_SYMBOL(mali_set_user_setting); +EXPORT_SYMBOL(mali_get_user_setting); + +static char mali_dev_name[] = "mali"; /* should be const, but the functions we call requires non-cost */ + +/* This driver only supports one Mali device, and this variable stores this single platform device */ +struct platform_device *mali_platform_device = NULL; + +/* This driver only supports one Mali device, and this variable stores the exposed misc device (/dev/mali) */ +static struct miscdevice mali_miscdevice = { 0, }; + +static int mali_miscdevice_register(struct platform_device *pdev); +static void mali_miscdevice_unregister(void); + +static int mali_open(struct inode *inode, struct file *filp); +static int mali_release(struct inode *inode, struct file *filp); +#ifdef HAVE_UNLOCKED_IOCTL +static long mali_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +#else +static int mali_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); +#endif + +static int mali_probe(struct platform_device *pdev); +static int mali_remove(struct platform_device *pdev); + +static int mali_driver_suspend_scheduler(struct device *dev); +static int mali_driver_resume_scheduler(struct device *dev); + +#ifdef CONFIG_PM_RUNTIME +static int mali_driver_runtime_suspend(struct device *dev); +static int mali_driver_runtime_resume(struct device *dev); +static int mali_driver_runtime_idle(struct device *dev); +#endif + +#if defined(MALI_FAKE_PLATFORM_DEVICE) +extern int mali_platform_device_register(void); +extern int mali_platform_device_unregister(void); +#endif + +/* Linux power management operations provided by the Mali device driver */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)) +struct pm_ext_ops mali_dev_ext_pm_ops = { + .base = + { + .suspend = mali_driver_suspend_scheduler, + .resume = mali_driver_resume_scheduler, + .freeze = mali_driver_suspend_scheduler, + .thaw = mali_driver_resume_scheduler, + }, +}; +#else +static const struct dev_pm_ops mali_dev_pm_ops = { +#ifdef CONFIG_PM_RUNTIME + .runtime_suspend = mali_driver_runtime_suspend, + .runtime_resume = mali_driver_runtime_resume, + .runtime_idle = mali_driver_runtime_idle, +#endif + .suspend = mali_driver_suspend_scheduler, + .resume = mali_driver_resume_scheduler, + .freeze = mali_driver_suspend_scheduler, + .thaw = mali_driver_resume_scheduler, + .poweroff = mali_driver_suspend_scheduler, +}; +#endif + +/* The Mali device driver struct */ +static struct platform_driver mali_platform_driver = { + .probe = mali_probe, + .remove = mali_remove, +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)) + .pm = &mali_dev_ext_pm_ops, +#endif + .driver = + { + .name = MALI_GPU_NAME_UTGARD, + .owner = THIS_MODULE, + .bus = &platform_bus_type, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) + .pm = &mali_dev_pm_ops, +#endif + }, +}; + +/* Linux misc device operations (/dev/mali) */ +struct file_operations mali_fops = { + .owner = THIS_MODULE, + .open = mali_open, + .release = mali_release, +#ifdef HAVE_UNLOCKED_IOCTL + .unlocked_ioctl = mali_ioctl, +#else + .ioctl = mali_ioctl, +#endif + .mmap = mali_mmap +}; + + +#if MALI_ENABLE_CPU_CYCLES +void mali_init_cpu_time_counters(int reset, int enable_divide_by_64) +{ + /* The CPU assembly reference used is: ARM Architecture Reference Manual ARMv7-AR C.b */ + u32 write_value; + + /* See B4.1.116 PMCNTENSET, Performance Monitors Count Enable Set register, VMSA */ + /* setting p15 c9 c12 1 to 0x8000000f==CPU_CYCLE_ENABLE |EVENT_3_ENABLE|EVENT_2_ENABLE|EVENT_1_ENABLE|EVENT_0_ENABLE */ + asm volatile("mcr p15, 0, %0, c9, c12, 1" :: "r"(0x8000000f)); + + + /* See B4.1.117 PMCR, Performance Monitors Control Register. Writing to p15, c9, c12, 0 */ + write_value = 1<<0; /* Bit 0 set. Enable counters */ + if (reset) { + write_value |= 1<<1; /* Reset event counters */ + write_value |= 1<<2; /* Reset cycle counter */ + } + if (enable_divide_by_64) { + write_value |= 1<<3; /* Enable the Clock divider by 64 */ + } + write_value |= 1<<4; /* Export enable. Not needed */ + asm volatile ("MCR p15, 0, %0, c9, c12, 0\t\n" :: "r"(write_value )); + + /* PMOVSR Overflow Flag Status Register - Clear Clock and Event overflows */ + asm volatile ("MCR p15, 0, %0, c9, c12, 3\t\n" :: "r"(0x8000000f)); + + + /* See B4.1.124 PMUSERENR - setting p15 c9 c14 to 1" */ + /* User mode access to the Performance Monitors enabled. */ + /* Lets User space read cpu clock cycles */ + asm volatile( "mcr p15, 0, %0, c9, c14, 0" :: "r"(1) ); +} + +/** A timer function that configures the cycle clock counter on current CPU. + The function \a mali_init_cpu_time_counters_on_all_cpus sets up this function + to trigger on all Cpus during module load. */ +static void mali_init_cpu_clock_timer_func(unsigned long data) +{ + int reset_counters, enable_divide_clock_counter_by_64; + int current_cpu = raw_smp_processor_id(); + unsigned int sample0; + unsigned int sample1; + + MALI_IGNORE(data); + + reset_counters= 1; + enable_divide_clock_counter_by_64 = 0; + mali_init_cpu_time_counters(reset_counters, enable_divide_clock_counter_by_64); + + sample0 = mali_get_cpu_cyclecount(); + sample1 = mali_get_cpu_cyclecount(); + + MALI_DEBUG_PRINT(3, ("Init Cpu %d cycle counter- First two samples: %08x %08x \n", current_cpu, sample0, sample1)); +} + +/** A timer functions for storing current time on all cpus. + Used for checking if the clocks have similar values or if they are drifting. */ +static void mali_print_cpu_clock_timer_func(unsigned long data) +{ + int current_cpu = raw_smp_processor_id(); + unsigned int sample0; + + MALI_IGNORE(data); + sample0 = mali_get_cpu_cyclecount(); + if ( current_cpu<8 ) { + mali_cpu_clock_last_value[current_cpu] = sample0; + } +} + +/** Init the performance registers on all CPUs to count clock cycles. + For init \a print_only should be 0. + If \a print_only is 1, it will intead print the current clock value of all CPUs.*/ +void mali_init_cpu_time_counters_on_all_cpus(int print_only) +{ + int i = 0; + int cpu_number; + int jiffies_trigger; + int jiffies_wait; + + jiffies_wait = 2; + jiffies_trigger = jiffies + jiffies_wait; + + for ( i=0 ; i < 8 ; i++ ) { + init_timer(&mali_init_cpu_clock_timers[i]); + if (print_only) mali_init_cpu_clock_timers[i].function = mali_print_cpu_clock_timer_func; + else mali_init_cpu_clock_timers[i].function = mali_init_cpu_clock_timer_func; + mali_init_cpu_clock_timers[i].expires = jiffies_trigger ; + } + cpu_number = cpumask_first(cpu_online_mask); + for ( i=0 ; i < 8 ; i++ ) { + int next_cpu; + add_timer_on(&mali_init_cpu_clock_timers[i], cpu_number); + next_cpu = cpumask_next(cpu_number, cpu_online_mask); + if (next_cpu >= nr_cpu_ids) break; + cpu_number = next_cpu; + } + + while (jiffies_wait) jiffies_wait= schedule_timeout_uninterruptible(jiffies_wait); + + for ( i=0 ; i < 8 ; i++ ) { + del_timer_sync(&mali_init_cpu_clock_timers[i]); + } + + if (print_only) { + if ( (0==mali_cpu_clock_last_value[2]) && (0==mali_cpu_clock_last_value[3]) ) { + /* Diff can be printed if we want to check if the clocks are in sync + int diff = mali_cpu_clock_last_value[0] - mali_cpu_clock_last_value[1];*/ + MALI_DEBUG_PRINT(2, ("CPU cycle counters readout all: %08x %08x\n", mali_cpu_clock_last_value[0], mali_cpu_clock_last_value[1])); + } else { + MALI_DEBUG_PRINT(2, ("CPU cycle counters readout all: %08x %08x %08x %08x\n", mali_cpu_clock_last_value[0], mali_cpu_clock_last_value[1], mali_cpu_clock_last_value[2], mali_cpu_clock_last_value[3] )); + } + } +} +#endif + + +int mali_module_init(void) +{ + int err = 0; + + MALI_DEBUG_PRINT(2, ("Inserting Mali v%d device driver. \n",_MALI_API_VERSION)); + MALI_DEBUG_PRINT(2, ("Compiled: %s, time: %s.\n", __DATE__, __TIME__)); + MALI_DEBUG_PRINT(2, ("Driver revision: %s\n", SVN_REV_STRING)); + +#if MALI_ENABLE_CPU_CYCLES + mali_init_cpu_time_counters_on_all_cpus(0); + MALI_DEBUG_PRINT(2, ("CPU cycle counter setup complete\n")); + /* Printing the current cpu counters */ + mali_init_cpu_time_counters_on_all_cpus(1); +#endif + + /* Initialize module wide settings */ +#if defined(MALI_FAKE_PLATFORM_DEVICE) + MALI_DEBUG_PRINT(2, ("mali_module_init() registering device\n")); + err = mali_platform_device_register(); + if (0 != err) { + return err; + } +#endif + + MALI_DEBUG_PRINT(2, ("mali_module_init() registering driver\n")); + + err = platform_driver_register(&mali_platform_driver); + + if (0 != err) { + MALI_DEBUG_PRINT(2, ("mali_module_init() Failed to register driver (%d)\n", err)); +#if defined(MALI_FAKE_PLATFORM_DEVICE) + mali_platform_device_unregister(); +#endif + mali_platform_device = NULL; + return err; + } + +#if defined(CONFIG_MALI400_INTERNAL_PROFILING) + err = _mali_internal_profiling_init(mali_boot_profiling ? MALI_TRUE : MALI_FALSE); + if (0 != err) { + /* No biggie if we wheren't able to initialize the profiling */ + MALI_PRINT_ERROR(("Failed to initialize profiling, feature will be unavailable\n")); + } +#endif + + MALI_PRINT(("Mali device driver loaded\n")); + + return 0; /* Success */ +} + +void mali_module_exit(void) +{ + MALI_DEBUG_PRINT(2, ("Unloading Mali v%d device driver.\n",_MALI_API_VERSION)); + + MALI_DEBUG_PRINT(2, ("mali_module_exit() unregistering driver\n")); + +#if defined(CONFIG_MALI400_INTERNAL_PROFILING) + _mali_internal_profiling_term(); +#endif + + platform_driver_unregister(&mali_platform_driver); + +#if defined(MALI_FAKE_PLATFORM_DEVICE) + MALI_DEBUG_PRINT(2, ("mali_module_exit() unregistering device\n")); + mali_platform_device_unregister(); +#endif + + MALI_PRINT(("Mali device driver unloaded\n")); +} + +static int mali_probe(struct platform_device *pdev) +{ + int err; + + MALI_DEBUG_PRINT(2, ("mali_probe(): Called for platform device %s\n", pdev->name)); + + if (NULL != mali_platform_device) { + /* Already connected to a device, return error */ + MALI_PRINT_ERROR(("mali_probe(): The Mali driver is already connected with a Mali device.")); + return -EEXIST; + } + + mali_platform_device = pdev; + + if (_MALI_OSK_ERR_OK == _mali_osk_wq_init()) { + /* Initialize the Mali GPU HW specified by pdev */ + if (_MALI_OSK_ERR_OK == mali_initialize_subsystems()) { + /* Register a misc device (so we are accessible from user space) */ + err = mali_miscdevice_register(pdev); + if (0 == err) { + /* Setup sysfs entries */ + err = mali_sysfs_register(mali_dev_name); + if (0 == err) { + MALI_DEBUG_PRINT(2, ("mali_probe(): Successfully initialized driver for platform device %s\n", pdev->name)); + return 0; + } else { + MALI_PRINT_ERROR(("mali_probe(): failed to register sysfs entries")); + } + mali_miscdevice_unregister(); + } else { + MALI_PRINT_ERROR(("mali_probe(): failed to register Mali misc device.")); + } + mali_terminate_subsystems(); + } else { + MALI_PRINT_ERROR(("mali_probe(): Failed to initialize Mali device driver.")); + } + _mali_osk_wq_term(); + } + + mali_platform_device = NULL; + return -EFAULT; +} + +static int mali_remove(struct platform_device *pdev) +{ + MALI_DEBUG_PRINT(2, ("mali_remove() called for platform device %s\n", pdev->name)); + mali_sysfs_unregister(); + mali_miscdevice_unregister(); + mali_terminate_subsystems(); + _mali_osk_wq_term(); + mali_platform_device = NULL; + return 0; +} + +static int mali_miscdevice_register(struct platform_device *pdev) +{ + int err; + + mali_miscdevice.minor = MISC_DYNAMIC_MINOR; + mali_miscdevice.name = mali_dev_name; + mali_miscdevice.fops = &mali_fops; + mali_miscdevice.parent = get_device(&pdev->dev); + + err = misc_register(&mali_miscdevice); + if (0 != err) { + MALI_PRINT_ERROR(("Failed to register misc device, misc_register() returned %d\n", err)); + } + + return err; +} + +static void mali_miscdevice_unregister(void) +{ + misc_deregister(&mali_miscdevice); +} + +static int mali_driver_suspend_scheduler(struct device *dev) +{ + mali_pm_os_suspend(); + return 0; +} + +static int mali_driver_resume_scheduler(struct device *dev) +{ + mali_pm_os_resume(); + return 0; +} + +#ifdef CONFIG_PM_RUNTIME +static int mali_driver_runtime_suspend(struct device *dev) +{ + mali_pm_runtime_suspend(); + return 0; +} + +static int mali_driver_runtime_resume(struct device *dev) +{ + mali_pm_runtime_resume(); + return 0; +} + +static int mali_driver_runtime_idle(struct device *dev) +{ + /* Nothing to do */ + return 0; +} +#endif + +static int mali_open(struct inode *inode, struct file *filp) +{ + struct mali_session_data * session_data; + _mali_osk_errcode_t err; + + /* input validation */ + if (mali_miscdevice.minor != iminor(inode)) { + MALI_PRINT_ERROR(("mali_open() Minor does not match\n")); + return -ENODEV; + } + + /* allocated struct to track this session */ + err = _mali_ukk_open((void **)&session_data); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + /* initialize file pointer */ + filp->f_pos = 0; + + /* link in our session data */ + filp->private_data = (void*)session_data; + + return 0; +} + +static int mali_release(struct inode *inode, struct file *filp) +{ + _mali_osk_errcode_t err; + + /* input validation */ + if (mali_miscdevice.minor != iminor(inode)) { + MALI_PRINT_ERROR(("mali_release() Minor does not match\n")); + return -ENODEV; + } + + err = _mali_ukk_close((void **)&filp->private_data); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + return 0; +} + +int map_errcode( _mali_osk_errcode_t err ) +{ + switch(err) { + case _MALI_OSK_ERR_OK : + return 0; + case _MALI_OSK_ERR_FAULT: + return -EFAULT; + case _MALI_OSK_ERR_INVALID_FUNC: + return -ENOTTY; + case _MALI_OSK_ERR_INVALID_ARGS: + return -EINVAL; + case _MALI_OSK_ERR_NOMEM: + return -ENOMEM; + case _MALI_OSK_ERR_TIMEOUT: + return -ETIMEDOUT; + case _MALI_OSK_ERR_RESTARTSYSCALL: + return -ERESTARTSYS; + case _MALI_OSK_ERR_ITEM_NOT_FOUND: + return -ENOENT; + default: + return -EFAULT; + } +} + +#ifdef HAVE_UNLOCKED_IOCTL +static long mali_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +#else +static int mali_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +#endif +{ + int err; + struct mali_session_data *session_data; + +#ifndef HAVE_UNLOCKED_IOCTL + /* inode not used */ + (void)inode; +#endif + + MALI_DEBUG_PRINT(7, ("Ioctl received 0x%08X 0x%08lX\n", cmd, arg)); + + session_data = (struct mali_session_data *)filp->private_data; + if (NULL == session_data) { + MALI_DEBUG_PRINT(7, ("filp->private_data was NULL\n")); + return -ENOTTY; + } + + if (NULL == (void *)arg) { + MALI_DEBUG_PRINT(7, ("arg was NULL\n")); + return -ENOTTY; + } + + switch(cmd) { + case MALI_IOC_WAIT_FOR_NOTIFICATION: + err = wait_for_notification_wrapper(session_data, (_mali_uk_wait_for_notification_s __user *)arg); + break; + + case MALI_IOC_GET_API_VERSION: + err = get_api_version_wrapper(session_data, (_mali_uk_get_api_version_s __user *)arg); + break; + + case MALI_IOC_POST_NOTIFICATION: + err = post_notification_wrapper(session_data, (_mali_uk_post_notification_s __user *)arg); + break; + + case MALI_IOC_GET_USER_SETTINGS: + err = get_user_settings_wrapper(session_data, (_mali_uk_get_user_settings_s __user *)arg); + break; + + case MALI_IOC_REQUEST_HIGH_PRIORITY: + err = request_high_priority_wrapper(session_data, (_mali_uk_request_high_priority_s __user *)arg); + break; + +#if defined(CONFIG_MALI400_PROFILING) + case MALI_IOC_PROFILING_START: + err = profiling_start_wrapper(session_data, (_mali_uk_profiling_start_s __user *)arg); + break; + + case MALI_IOC_PROFILING_ADD_EVENT: + err = profiling_add_event_wrapper(session_data, (_mali_uk_profiling_add_event_s __user *)arg); + break; + + case MALI_IOC_PROFILING_STOP: + err = profiling_stop_wrapper(session_data, (_mali_uk_profiling_stop_s __user *)arg); + break; + + case MALI_IOC_PROFILING_GET_EVENT: + err = profiling_get_event_wrapper(session_data, (_mali_uk_profiling_get_event_s __user *)arg); + break; + + case MALI_IOC_PROFILING_CLEAR: + err = profiling_clear_wrapper(session_data, (_mali_uk_profiling_clear_s __user *)arg); + break; + + case MALI_IOC_PROFILING_GET_CONFIG: + /* Deprecated: still compatible with get_user_settings */ + err = get_user_settings_wrapper(session_data, (_mali_uk_get_user_settings_s __user *)arg); + break; + + case MALI_IOC_PROFILING_REPORT_SW_COUNTERS: + err = profiling_report_sw_counters_wrapper(session_data, (_mali_uk_sw_counters_report_s __user *)arg); + break; + +#else + + case MALI_IOC_PROFILING_START: /* FALL-THROUGH */ + case MALI_IOC_PROFILING_ADD_EVENT: /* FALL-THROUGH */ + case MALI_IOC_PROFILING_STOP: /* FALL-THROUGH */ + case MALI_IOC_PROFILING_GET_EVENT: /* FALL-THROUGH */ + case MALI_IOC_PROFILING_CLEAR: /* FALL-THROUGH */ + case MALI_IOC_PROFILING_GET_CONFIG: /* FALL-THROUGH */ + case MALI_IOC_PROFILING_REPORT_SW_COUNTERS: /* FALL-THROUGH */ + MALI_DEBUG_PRINT(2, ("Profiling not supported\n")); + err = -ENOTTY; + break; + +#endif + + case MALI_IOC_MEM_WRITE_SAFE: + err = mem_write_safe_wrapper(session_data, (_mali_uk_mem_write_safe_s __user *)arg); + break; + + case MALI_IOC_MEM_MAP_EXT: + err = mem_map_ext_wrapper(session_data, (_mali_uk_map_external_mem_s __user *)arg); + break; + + case MALI_IOC_MEM_UNMAP_EXT: + err = mem_unmap_ext_wrapper(session_data, (_mali_uk_unmap_external_mem_s __user *)arg); + break; + + case MALI_IOC_MEM_QUERY_MMU_PAGE_TABLE_DUMP_SIZE: + err = mem_query_mmu_page_table_dump_size_wrapper(session_data, (_mali_uk_query_mmu_page_table_dump_size_s __user *)arg); + break; + + case MALI_IOC_MEM_DUMP_MMU_PAGE_TABLE: + err = mem_dump_mmu_page_table_wrapper(session_data, (_mali_uk_dump_mmu_page_table_s __user *)arg); + break; + +#if defined(CONFIG_MALI400_UMP) + + case MALI_IOC_MEM_ATTACH_UMP: + err = mem_attach_ump_wrapper(session_data, (_mali_uk_attach_ump_mem_s __user *)arg); + break; + + case MALI_IOC_MEM_RELEASE_UMP: + err = mem_release_ump_wrapper(session_data, (_mali_uk_release_ump_mem_s __user *)arg); + break; + +#else + + case MALI_IOC_MEM_ATTACH_UMP: + case MALI_IOC_MEM_RELEASE_UMP: /* FALL-THROUGH */ + MALI_DEBUG_PRINT(2, ("UMP not supported\n")); + err = -ENOTTY; + break; +#endif + +#ifdef CONFIG_DMA_SHARED_BUFFER + case MALI_IOC_MEM_ATTACH_DMA_BUF: + err = mali_attach_dma_buf(session_data, (_mali_uk_attach_dma_buf_s __user *)arg); + break; + + case MALI_IOC_MEM_RELEASE_DMA_BUF: + err = mali_release_dma_buf(session_data, (_mali_uk_release_dma_buf_s __user *)arg); + break; + + case MALI_IOC_MEM_DMA_BUF_GET_SIZE: + err = mali_dma_buf_get_size(session_data, (_mali_uk_dma_buf_get_size_s __user *)arg); + break; +#else + + case MALI_IOC_MEM_ATTACH_DMA_BUF: /* FALL-THROUGH */ + case MALI_IOC_MEM_RELEASE_DMA_BUF: /* FALL-THROUGH */ + case MALI_IOC_MEM_DMA_BUF_GET_SIZE: /* FALL-THROUGH */ + MALI_DEBUG_PRINT(2, ("DMA-BUF not supported\n")); + err = -ENOTTY; + break; +#endif + + case MALI_IOC_PP_START_JOB: + err = pp_start_job_wrapper(session_data, (_mali_uk_pp_start_job_s __user *)arg); + break; + + case MALI_IOC_PP_AND_GP_START_JOB: + err = pp_and_gp_start_job_wrapper(session_data, (_mali_uk_pp_and_gp_start_job_s __user *)arg); + break; + + case MALI_IOC_PP_NUMBER_OF_CORES_GET: + err = pp_get_number_of_cores_wrapper(session_data, (_mali_uk_get_pp_number_of_cores_s __user *)arg); + break; + + case MALI_IOC_PP_CORE_VERSION_GET: + err = pp_get_core_version_wrapper(session_data, (_mali_uk_get_pp_core_version_s __user *)arg); + break; + + case MALI_IOC_PP_DISABLE_WB: + err = pp_disable_wb_wrapper(session_data, (_mali_uk_pp_disable_wb_s __user *)arg); + break; + + case MALI_IOC_GP2_START_JOB: + err = gp_start_job_wrapper(session_data, (_mali_uk_gp_start_job_s __user *)arg); + break; + + case MALI_IOC_GP2_NUMBER_OF_CORES_GET: + err = gp_get_number_of_cores_wrapper(session_data, (_mali_uk_get_gp_number_of_cores_s __user *)arg); + break; + + case MALI_IOC_GP2_CORE_VERSION_GET: + err = gp_get_core_version_wrapper(session_data, (_mali_uk_get_gp_core_version_s __user *)arg); + break; + + case MALI_IOC_GP2_SUSPEND_RESPONSE: + err = gp_suspend_response_wrapper(session_data, (_mali_uk_gp_suspend_response_s __user *)arg); + break; + + case MALI_IOC_VSYNC_EVENT_REPORT: + err = vsync_event_report_wrapper(session_data, (_mali_uk_vsync_event_report_s __user *)arg); + break; + + case MALI_IOC_TIMELINE_GET_LATEST_POINT: + err = timeline_get_latest_point_wrapper(session_data, (_mali_uk_timeline_get_latest_point_s __user *)arg); + break; + case MALI_IOC_TIMELINE_WAIT: + err = timeline_wait_wrapper(session_data, (_mali_uk_timeline_wait_s __user *)arg); + break; + case MALI_IOC_TIMELINE_CREATE_SYNC_FENCE: + err = timeline_create_sync_fence_wrapper(session_data, (_mali_uk_timeline_create_sync_fence_s __user *)arg); + break; + case MALI_IOC_SOFT_JOB_START: + err = soft_job_start_wrapper(session_data, (_mali_uk_soft_job_start_s __user *)arg); + break; + case MALI_IOC_SOFT_JOB_SIGNAL: + err = soft_job_signal_wrapper(session_data, (_mali_uk_soft_job_signal_s __user *)arg); + break; + + case MALI_IOC_MEM_INIT: /* Fallthrough */ + case MALI_IOC_MEM_TERM: /* Fallthrough */ + MALI_DEBUG_PRINT(2, ("Deprecated ioctls called\n")); + err = -ENOTTY; + break; + + case MALI_IOC_MEM_GET_BIG_BLOCK: /* Fallthrough */ + case MALI_IOC_MEM_FREE_BIG_BLOCK: + MALI_PRINT_ERROR(("Non-MMU mode is no longer supported.\n")); + err = -ENOTTY; + break; + + default: + MALI_DEBUG_PRINT(2, ("No handler for ioctl 0x%08X 0x%08lX\n", cmd, arg)); + err = -ENOTTY; + }; + + return err; +} + + +module_init(mali_module_init); +module_exit(mali_module_exit); + +MODULE_LICENSE(MALI_KERNEL_LINUX_LICENSE); +MODULE_AUTHOR("ARM Ltd."); +MODULE_VERSION(SVN_REV_STRING); diff --git a/drivers/gpu/arm/mali/linux/mali_kernel_linux.h b/drivers/gpu/arm/mali/linux/mali_kernel_linux.h new file mode 100644 index 00000000000000..cf8e2564c154cb --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_kernel_linux.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_LINUX_H__ +#define __MALI_KERNEL_LINUX_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include /* character device definitions */ +#include "mali_kernel_license.h" +#include "mali_osk_types.h" + +extern struct platform_device *mali_platform_device; + +#if MALI_LICENSE_IS_GPL +/* Defined in mali_osk_irq.h */ +extern struct workqueue_struct * mali_wq_normal; +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_KERNEL_LINUX_H__ */ diff --git a/drivers/gpu/arm/mali/linux/mali_kernel_sysfs.c b/drivers/gpu/arm/mali/linux/mali_kernel_sysfs.c new file mode 100644 index 00000000000000..026e2415ded2c4 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_kernel_sysfs.c @@ -0,0 +1,1390 @@ +/** + * Copyright (C) 2011-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +/** + * @file mali_kernel_sysfs.c + * Implementation of some sysfs data exports + */ + +#include +#include +#include +#include +#include "mali_kernel_license.h" +#include "mali_kernel_common.h" +#include "mali_ukk.h" + +#if MALI_LICENSE_IS_GPL + +#include +#include +#include +#include +#include +#include "mali_kernel_sysfs.h" +#if defined(CONFIG_MALI400_INTERNAL_PROFILING) +#include +#include "mali_osk_profiling.h" +#endif + +#include +#include "mali_pm.h" +#include "mali_pmu.h" +#include "mali_group.h" +#include "mali_gp.h" +#include "mali_pp.h" +#include "mali_l2_cache.h" +#include "mali_hw_core.h" +#include "mali_kernel_core.h" +#include "mali_user_settings_db.h" +#include "mali_profiling_internal.h" +#include "mali_gp_job.h" +#include "mali_pp_job.h" +#include "mali_pp_scheduler.h" + +#define PRIVATE_DATA_COUNTER_MAKE_GP(src) (src) +#define PRIVATE_DATA_COUNTER_MAKE_PP(src) ((1 << 24) | src) +#define PRIVATE_DATA_COUNTER_MAKE_PP_SUB_JOB(src, sub_job) ((1 << 24) | (1 << 16) | (sub_job << 8) | src) +#define PRIVATE_DATA_COUNTER_IS_PP(a) ((((a) >> 24) & 0xFF) ? MALI_TRUE : MALI_FALSE) +#define PRIVATE_DATA_COUNTER_GET_SRC(a) (a & 0xFF) +#define PRIVATE_DATA_COUNTER_IS_SUB_JOB(a) ((((a) >> 16) & 0xFF) ? MALI_TRUE : MALI_FALSE) +#define PRIVATE_DATA_COUNTER_GET_SUB_JOB(a) (((a) >> 8) & 0xFF) + +#define POWER_BUFFER_SIZE 3 + +static struct dentry *mali_debugfs_dir = NULL; + +typedef enum { + _MALI_DEVICE_SUSPEND, + _MALI_DEVICE_RESUME, + _MALI_DEVICE_DVFS_PAUSE, + _MALI_DEVICE_DVFS_RESUME, + _MALI_MAX_EVENTS +} _mali_device_debug_power_events; + +static const char* const mali_power_events[_MALI_MAX_EVENTS] = { + [_MALI_DEVICE_SUSPEND] = "suspend", + [_MALI_DEVICE_RESUME] = "resume", + [_MALI_DEVICE_DVFS_PAUSE] = "dvfs_pause", + [_MALI_DEVICE_DVFS_RESUME] = "dvfs_resume", +}; + +static mali_bool power_always_on_enabled = MALI_FALSE; + +static int open_copy_private_data(struct inode *inode, struct file *filp) +{ + filp->private_data = inode->i_private; + return 0; +} + +static ssize_t group_enabled_read(struct file *filp, char __user *buf, size_t count, loff_t *offp) +{ + int r; + char buffer[64]; + struct mali_group *group; + + group = (struct mali_group *)filp->private_data; + MALI_DEBUG_ASSERT_POINTER(group); + + r = sprintf(buffer, "%u\n", mali_group_is_enabled(group) ? 1 : 0); + + return simple_read_from_buffer(buf, count, offp, buffer, r); +} + +static ssize_t group_enabled_write(struct file *filp, const char __user *buf, size_t count, loff_t *offp) +{ + int r; + char buffer[64]; + unsigned long val; + struct mali_group *group; + + group = (struct mali_group *)filp->private_data; + MALI_DEBUG_ASSERT_POINTER(group); + + if (count >= sizeof(buffer)) { + return -ENOMEM; + } + + if (copy_from_user(&buffer[0], buf, count)) { + return -EFAULT; + } + buffer[count] = '\0'; + + r = strict_strtoul(&buffer[0], 10, &val); + if (0 != r) { + return -EINVAL; + } + + switch (val) { + case 1: + mali_group_enable(group); + break; + case 0: + mali_group_disable(group); + break; + default: + return -EINVAL; + break; + } + + *offp += count; + return count; +} + +static const struct file_operations group_enabled_fops = { + .owner = THIS_MODULE, + .open = open_copy_private_data, + .read = group_enabled_read, + .write = group_enabled_write, +}; + +static ssize_t hw_core_base_addr_read(struct file *filp, char __user *buf, size_t count, loff_t *offp) +{ + int r; + char buffer[64]; + struct mali_hw_core *hw_core; + + hw_core = (struct mali_hw_core *)filp->private_data; + MALI_DEBUG_ASSERT_POINTER(hw_core); + + r = sprintf(buffer, "0x%08X\n", hw_core->phys_addr); + + return simple_read_from_buffer(buf, count, offp, buffer, r); +} + +static const struct file_operations hw_core_base_addr_fops = { + .owner = THIS_MODULE, + .open = open_copy_private_data, + .read = hw_core_base_addr_read, +}; + +static ssize_t profiling_counter_src_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) +{ + u32 is_pp = PRIVATE_DATA_COUNTER_IS_PP((u32)filp->private_data); + u32 src_id = PRIVATE_DATA_COUNTER_GET_SRC((u32)filp->private_data); + mali_bool is_sub_job = PRIVATE_DATA_COUNTER_IS_SUB_JOB((u32)filp->private_data); + u32 sub_job = PRIVATE_DATA_COUNTER_GET_SUB_JOB((u32)filp->private_data); + char buf[64]; + int r; + u32 val; + + if (MALI_TRUE == is_pp) { + /* PP counter */ + if (MALI_TRUE == is_sub_job) { + /* Get counter for a particular sub job */ + if (0 == src_id) { + val = mali_pp_job_get_pp_counter_sub_job_src0(sub_job); + } else { + val = mali_pp_job_get_pp_counter_sub_job_src1(sub_job); + } + } else { + /* Get default counter for all PP sub jobs */ + if (0 == src_id) { + val = mali_pp_job_get_pp_counter_global_src0(); + } else { + val = mali_pp_job_get_pp_counter_global_src1(); + } + } + } else { + /* GP counter */ + if (0 == src_id) { + val = mali_gp_job_get_gp_counter_src0(); + } else { + val = mali_gp_job_get_gp_counter_src1(); + } + } + + if (MALI_HW_CORE_NO_COUNTER == val) { + r = sprintf(buf, "-1\n"); + } else { + r = sprintf(buf, "%u\n", val); + } + + return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); +} + +static ssize_t profiling_counter_src_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + u32 is_pp = PRIVATE_DATA_COUNTER_IS_PP((u32)filp->private_data); + u32 src_id = PRIVATE_DATA_COUNTER_GET_SRC((u32)filp->private_data); + mali_bool is_sub_job = PRIVATE_DATA_COUNTER_IS_SUB_JOB((u32)filp->private_data); + u32 sub_job = PRIVATE_DATA_COUNTER_GET_SUB_JOB((u32)filp->private_data); + char buf[64]; + long val; + int ret; + + if (cnt >= sizeof(buf)) { + return -EINVAL; + } + + if (copy_from_user(&buf, ubuf, cnt)) { + return -EFAULT; + } + + buf[cnt] = 0; + + ret = strict_strtol(buf, 10, &val); + if (ret < 0) { + return ret; + } + + if (val < 0) { + /* any negative input will disable counter */ + val = MALI_HW_CORE_NO_COUNTER; + } + + if (MALI_TRUE == is_pp) { + /* PP counter */ + if (MALI_TRUE == is_sub_job) { + /* Set counter for a particular sub job */ + if (0 == src_id) { + mali_pp_job_set_pp_counter_sub_job_src0(sub_job, (u32)val); + } else { + mali_pp_job_set_pp_counter_sub_job_src1(sub_job, (u32)val); + } + } else { + /* Set default counter for all PP sub jobs */ + if (0 == src_id) { + mali_pp_job_set_pp_counter_global_src0((u32)val); + } else { + mali_pp_job_set_pp_counter_global_src1((u32)val); + } + } + } else { + /* GP counter */ + if (0 == src_id) { + mali_gp_job_set_gp_counter_src0((u32)val); + } else { + mali_gp_job_set_gp_counter_src1((u32)val); + } + } + + *ppos += cnt; + return cnt; +} + +static const struct file_operations profiling_counter_src_fops = { + .owner = THIS_MODULE, + .open = open_copy_private_data, + .read = profiling_counter_src_read, + .write = profiling_counter_src_write, +}; + +static ssize_t l2_l2x_counter_srcx_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos, u32 src_id) +{ + char buf[64]; + int r; + u32 val; + struct mali_l2_cache_core *l2_core = (struct mali_l2_cache_core *)filp->private_data; + + if (0 == src_id) { + val = mali_l2_cache_core_get_counter_src0(l2_core); + } else { + val = mali_l2_cache_core_get_counter_src1(l2_core); + } + + if (MALI_HW_CORE_NO_COUNTER == val) { + r = sprintf(buf, "-1\n"); + } else { + r = sprintf(buf, "%u\n", val); + } + return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); +} + +static ssize_t l2_l2x_counter_srcx_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos, u32 src_id) +{ + struct mali_l2_cache_core *l2_core = (struct mali_l2_cache_core *)filp->private_data; + char buf[64]; + long val; + int ret; + + if (cnt >= sizeof(buf)) { + return -EINVAL; + } + + if (copy_from_user(&buf, ubuf, cnt)) { + return -EFAULT; + } + + buf[cnt] = 0; + + ret = strict_strtol(buf, 10, &val); + if (ret < 0) { + return ret; + } + + if (val < 0) { + /* any negative input will disable counter */ + val = MALI_HW_CORE_NO_COUNTER; + } + + if (0 == src_id) { + mali_l2_cache_core_set_counter_src0(l2_core, (u32)val); + } else { + mali_l2_cache_core_set_counter_src1(l2_core, (u32)val); + } + + *ppos += cnt; + return cnt; +} + +static ssize_t l2_all_counter_srcx_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos, u32 src_id) +{ + char buf[64]; + long val; + int ret; + u32 l2_id; + struct mali_l2_cache_core *l2_cache; + + if (cnt >= sizeof(buf)) { + return -EINVAL; + } + + if (copy_from_user(&buf, ubuf, cnt)) { + return -EFAULT; + } + + buf[cnt] = 0; + + ret = strict_strtol(buf, 10, &val); + if (ret < 0) { + return ret; + } + + if (val < 0) { + /* any negative input will disable counter */ + val = MALI_HW_CORE_NO_COUNTER; + } + + l2_id = 0; + l2_cache = mali_l2_cache_core_get_glob_l2_core(l2_id); + while (NULL != l2_cache) { + if (0 == src_id) { + mali_l2_cache_core_set_counter_src0(l2_cache, (u32)val); + } else { + mali_l2_cache_core_set_counter_src1(l2_cache, (u32)val); + } + + /* try next L2 */ + l2_id++; + l2_cache = mali_l2_cache_core_get_glob_l2_core(l2_id); + } + + *ppos += cnt; + return cnt; +} + +static ssize_t l2_l2x_counter_src0_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) +{ + return l2_l2x_counter_srcx_read(filp, ubuf, cnt, ppos, 0); +} + +static ssize_t l2_l2x_counter_src1_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) +{ + return l2_l2x_counter_srcx_read(filp, ubuf, cnt, ppos, 1); +} + +static ssize_t l2_l2x_counter_src0_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + return l2_l2x_counter_srcx_write(filp, ubuf, cnt, ppos, 0); +} + +static ssize_t l2_l2x_counter_src1_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + return l2_l2x_counter_srcx_write(filp, ubuf, cnt, ppos, 1); +} + +static ssize_t l2_all_counter_src0_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + return l2_all_counter_srcx_write(filp, ubuf, cnt, ppos, 0); +} + +static ssize_t l2_all_counter_src1_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + return l2_all_counter_srcx_write(filp, ubuf, cnt, ppos, 1); +} + +static const struct file_operations l2_l2x_counter_src0_fops = { + .owner = THIS_MODULE, + .open = open_copy_private_data, + .read = l2_l2x_counter_src0_read, + .write = l2_l2x_counter_src0_write, +}; + +static const struct file_operations l2_l2x_counter_src1_fops = { + .owner = THIS_MODULE, + .open = open_copy_private_data, + .read = l2_l2x_counter_src1_read, + .write = l2_l2x_counter_src1_write, +}; + +static const struct file_operations l2_all_counter_src0_fops = { + .owner = THIS_MODULE, + .write = l2_all_counter_src0_write, +}; + +static const struct file_operations l2_all_counter_src1_fops = { + .owner = THIS_MODULE, + .write = l2_all_counter_src1_write, +}; + +static ssize_t power_always_on_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + unsigned long val; + int ret; + char buf[32]; + + cnt = min(cnt, sizeof(buf) - 1); + if (copy_from_user(buf, ubuf, cnt)) { + return -EFAULT; + } + buf[cnt] = '\0'; + + ret = strict_strtoul(buf, 10, &val); + if (0 != ret) { + return ret; + } + + /* Update setting (not exactly thread safe) */ + if (1 == val && MALI_FALSE == power_always_on_enabled) { + power_always_on_enabled = MALI_TRUE; + _mali_osk_pm_dev_ref_add(); + } else if (0 == val && MALI_TRUE == power_always_on_enabled) { + power_always_on_enabled = MALI_FALSE; + _mali_osk_pm_dev_ref_dec(); + } + + *ppos += cnt; + return cnt; +} + +static ssize_t power_always_on_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) +{ + if (MALI_TRUE == power_always_on_enabled) { + return simple_read_from_buffer(ubuf, cnt, ppos, "1\n", 2); + } else { + return simple_read_from_buffer(ubuf, cnt, ppos, "0\n", 2); + } +} + +static const struct file_operations power_always_on_fops = { + .owner = THIS_MODULE, + .read = power_always_on_read, + .write = power_always_on_write, +}; + +static ssize_t power_power_events_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + + if (!strncmp(ubuf,mali_power_events[_MALI_DEVICE_SUSPEND],strlen(mali_power_events[_MALI_DEVICE_SUSPEND]))) { + mali_pm_os_suspend(); + + } else if (!strncmp(ubuf,mali_power_events[_MALI_DEVICE_RESUME],strlen(mali_power_events[_MALI_DEVICE_RESUME]))) { + mali_pm_os_resume(); + } else if (!strncmp(ubuf,mali_power_events[_MALI_DEVICE_DVFS_PAUSE],strlen(mali_power_events[_MALI_DEVICE_DVFS_PAUSE]))) { + mali_dev_pause(); + } else if (!strncmp(ubuf,mali_power_events[_MALI_DEVICE_DVFS_RESUME],strlen(mali_power_events[_MALI_DEVICE_DVFS_RESUME]))) { + mali_dev_resume(); + } + *ppos += cnt; + return cnt; +} + +static loff_t power_power_events_seek(struct file *file, loff_t offset, int orig) +{ + file->f_pos = offset; + return 0; +} + +static const struct file_operations power_power_events_fops = { + .owner = THIS_MODULE, + .write = power_power_events_write, + .llseek = power_power_events_seek, +}; + +#if MALI_STATE_TRACKING +static int mali_seq_internal_state_show(struct seq_file *seq_file, void *v) +{ + u32 len = 0; + u32 size; + char *buf; + + size = seq_get_buf(seq_file, &buf); + + if(!size) { + return -ENOMEM; + } + + /* Create the internal state dump. */ + len = snprintf(buf+len, size-len, "Mali device driver %s\n", SVN_REV_STRING); + len += snprintf(buf+len, size-len, "License: %s\n\n", MALI_KERNEL_LINUX_LICENSE); + + len += _mali_kernel_core_dump_state(buf + len, size - len); + + seq_commit(seq_file, len); + + return 0; +} + +static int mali_seq_internal_state_open(struct inode *inode, struct file *file) +{ + return single_open(file, mali_seq_internal_state_show, NULL); +} + +static const struct file_operations mali_seq_internal_state_fops = { + .owner = THIS_MODULE, + .open = mali_seq_internal_state_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif /* MALI_STATE_TRACKING */ + +#if defined(CONFIG_MALI400_INTERNAL_PROFILING) +static ssize_t profiling_record_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char buf[64]; + int r; + + r = sprintf(buf, "%u\n", _mali_internal_profiling_is_recording() ? 1 : 0); + return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); +} + +static ssize_t profiling_record_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char buf[64]; + unsigned long val; + int ret; + + if (cnt >= sizeof(buf)) { + return -EINVAL; + } + + if (copy_from_user(&buf, ubuf, cnt)) { + return -EFAULT; + } + + buf[cnt] = 0; + + ret = strict_strtoul(buf, 10, &val); + if (ret < 0) { + return ret; + } + + if (val != 0) { + u32 limit = MALI_PROFILING_MAX_BUFFER_ENTRIES; /* This can be made configurable at a later stage if we need to */ + + /* check if we are already recording */ + if (MALI_TRUE == _mali_internal_profiling_is_recording()) { + MALI_DEBUG_PRINT(3, ("Recording of profiling events already in progress\n")); + return -EFAULT; + } + + /* check if we need to clear out an old recording first */ + if (MALI_TRUE == _mali_internal_profiling_have_recording()) { + if (_MALI_OSK_ERR_OK != _mali_internal_profiling_clear()) { + MALI_DEBUG_PRINT(3, ("Failed to clear existing recording of profiling events\n")); + return -EFAULT; + } + } + + /* start recording profiling data */ + if (_MALI_OSK_ERR_OK != _mali_internal_profiling_start(&limit)) { + MALI_DEBUG_PRINT(3, ("Failed to start recording of profiling events\n")); + return -EFAULT; + } + + MALI_DEBUG_PRINT(3, ("Profiling recording started (max %u events)\n", limit)); + } else { + /* stop recording profiling data */ + u32 count = 0; + if (_MALI_OSK_ERR_OK != _mali_internal_profiling_stop(&count)) { + MALI_DEBUG_PRINT(2, ("Failed to stop recording of profiling events\n")); + return -EFAULT; + } + + MALI_DEBUG_PRINT(2, ("Profiling recording stopped (recorded %u events)\n", count)); + } + + *ppos += cnt; + return cnt; +} + +static const struct file_operations profiling_record_fops = { + .owner = THIS_MODULE, + .read = profiling_record_read, + .write = profiling_record_write, +}; + +static void *profiling_events_start(struct seq_file *s, loff_t *pos) +{ + loff_t *spos; + + /* check if we have data avaiable */ + if (MALI_TRUE != _mali_internal_profiling_have_recording()) { + return NULL; + } + + spos = kmalloc(sizeof(loff_t), GFP_KERNEL); + if (NULL == spos) { + return NULL; + } + + *spos = *pos; + return spos; +} + +static void *profiling_events_next(struct seq_file *s, void *v, loff_t *pos) +{ + loff_t *spos = v; + + /* check if we have data avaiable */ + if (MALI_TRUE != _mali_internal_profiling_have_recording()) { + return NULL; + } + + /* check if the next entry actually is avaiable */ + if (_mali_internal_profiling_get_count() <= (u32)(*spos + 1)) { + return NULL; + } + + *pos = ++*spos; + return spos; +} + +static void profiling_events_stop(struct seq_file *s, void *v) +{ + kfree(v); +} + +static int profiling_events_show(struct seq_file *seq_file, void *v) +{ + loff_t *spos = v; + u32 index; + u64 timestamp; + u32 event_id; + u32 data[5]; + + index = (u32)*spos; + + /* Retrieve all events */ + if (_MALI_OSK_ERR_OK == _mali_internal_profiling_get_event(index, ×tamp, &event_id, data)) { + seq_printf(seq_file, "%llu %u %u %u %u %u %u\n", timestamp, event_id, data[0], data[1], data[2], data[3], data[4]); + return 0; + } + + return 0; +} + +static int profiling_events_show_human_readable(struct seq_file *seq_file, void *v) +{ +#define MALI_EVENT_ID_IS_HW(event_id) (((event_id & 0x00FF0000) >= MALI_PROFILING_EVENT_CHANNEL_GP0) && ((event_id & 0x00FF0000) <= MALI_PROFILING_EVENT_CHANNEL_PP7)) + + static u64 start_time = 0; + loff_t *spos = v; + u32 index; + u64 timestamp; + u32 event_id; + u32 data[5]; + + index = (u32)*spos; + + /* Retrieve all events */ + if (_MALI_OSK_ERR_OK == _mali_internal_profiling_get_event(index, ×tamp, &event_id, data)) { + seq_printf(seq_file, "%llu %u %u %u %u %u %u # ", timestamp, event_id, data[0], data[1], data[2], data[3], data[4]); + + if (0 == index) { + start_time = timestamp; + } + + seq_printf(seq_file, "[%06u] ", index); + + switch(event_id & 0x0F000000) { + case MALI_PROFILING_EVENT_TYPE_SINGLE: + seq_printf(seq_file, "SINGLE | "); + break; + case MALI_PROFILING_EVENT_TYPE_START: + seq_printf(seq_file, "START | "); + break; + case MALI_PROFILING_EVENT_TYPE_STOP: + seq_printf(seq_file, "STOP | "); + break; + case MALI_PROFILING_EVENT_TYPE_SUSPEND: + seq_printf(seq_file, "SUSPEND | "); + break; + case MALI_PROFILING_EVENT_TYPE_RESUME: + seq_printf(seq_file, "RESUME | "); + break; + default: + seq_printf(seq_file, "0x%01X | ", (event_id & 0x0F000000) >> 24); + break; + } + + switch(event_id & 0x00FF0000) { + case MALI_PROFILING_EVENT_CHANNEL_SOFTWARE: + seq_printf(seq_file, "SW | "); + break; + case MALI_PROFILING_EVENT_CHANNEL_GP0: + seq_printf(seq_file, "GP0 | "); + break; + case MALI_PROFILING_EVENT_CHANNEL_PP0: + seq_printf(seq_file, "PP0 | "); + break; + case MALI_PROFILING_EVENT_CHANNEL_PP1: + seq_printf(seq_file, "PP1 | "); + break; + case MALI_PROFILING_EVENT_CHANNEL_PP2: + seq_printf(seq_file, "PP2 | "); + break; + case MALI_PROFILING_EVENT_CHANNEL_PP3: + seq_printf(seq_file, "PP3 | "); + break; + case MALI_PROFILING_EVENT_CHANNEL_PP4: + seq_printf(seq_file, "PP4 | "); + break; + case MALI_PROFILING_EVENT_CHANNEL_PP5: + seq_printf(seq_file, "PP5 | "); + break; + case MALI_PROFILING_EVENT_CHANNEL_PP6: + seq_printf(seq_file, "PP6 | "); + break; + case MALI_PROFILING_EVENT_CHANNEL_PP7: + seq_printf(seq_file, "PP7 | "); + break; + case MALI_PROFILING_EVENT_CHANNEL_GPU: + seq_printf(seq_file, "GPU | "); + break; + default: + seq_printf(seq_file, "0x%02X | ", (event_id & 0x00FF0000) >> 16); + break; + } + + if (MALI_EVENT_ID_IS_HW(event_id)) { + if (((event_id & 0x0F000000) == MALI_PROFILING_EVENT_TYPE_START) || ((event_id & 0x0F000000) == MALI_PROFILING_EVENT_TYPE_STOP)) { + switch(event_id & 0x0000FFFF) { + case MALI_PROFILING_EVENT_REASON_START_STOP_HW_PHYSICAL: + seq_printf(seq_file, "PHYSICAL | "); + break; + case MALI_PROFILING_EVENT_REASON_START_STOP_HW_VIRTUAL: + seq_printf(seq_file, "VIRTUAL | "); + break; + default: + seq_printf(seq_file, "0x%04X | ", event_id & 0x0000FFFF); + break; + } + } else { + seq_printf(seq_file, "0x%04X | ", event_id & 0x0000FFFF); + } + } else { + seq_printf(seq_file, "0x%04X | ", event_id & 0x0000FFFF); + } + + seq_printf(seq_file, "T0 + 0x%016llX\n", timestamp - start_time); + + return 0; + } + + return 0; +} + +static const struct seq_operations profiling_events_seq_ops = { + .start = profiling_events_start, + .next = profiling_events_next, + .stop = profiling_events_stop, + .show = profiling_events_show +}; + +static int profiling_events_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &profiling_events_seq_ops); +} + +static const struct file_operations profiling_events_fops = { + .owner = THIS_MODULE, + .open = profiling_events_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static const struct seq_operations profiling_events_human_readable_seq_ops = { + .start = profiling_events_start, + .next = profiling_events_next, + .stop = profiling_events_stop, + .show = profiling_events_show_human_readable +}; + +static int profiling_events_human_readable_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &profiling_events_human_readable_seq_ops); +} + +static const struct file_operations profiling_events_human_readable_fops = { + .owner = THIS_MODULE, + .open = profiling_events_human_readable_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +#endif + +static ssize_t memory_used_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char buf[64]; + size_t r; + u32 mem = _mali_ukk_report_memory_usage(); + + r = snprintf(buf, 64, "%u\n", mem); + return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); +} + +static const struct file_operations memory_usage_fops = { + .owner = THIS_MODULE, + .read = memory_used_read, +}; + +static ssize_t utilization_gp_pp_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char buf[64]; + size_t r; + u32 uval= _mali_ukk_utilization_gp_pp(); + + r = snprintf(buf, 64, "%u\n", uval); + return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); +} + +static ssize_t utilization_gp_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char buf[64]; + size_t r; + u32 uval= _mali_ukk_utilization_gp(); + + r = snprintf(buf, 64, "%u\n", uval); + return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); +} + +static ssize_t utilization_pp_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char buf[64]; + size_t r; + u32 uval= _mali_ukk_utilization_pp(); + + r = snprintf(buf, 64, "%u\n", uval); + return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); +} + + +static const struct file_operations utilization_gp_pp_fops = { + .owner = THIS_MODULE, + .read = utilization_gp_pp_read, +}; + +static const struct file_operations utilization_gp_fops = { + .owner = THIS_MODULE, + .read = utilization_gp_read, +}; + +static const struct file_operations utilization_pp_fops = { + .owner = THIS_MODULE, + .read = utilization_pp_read, +}; + +static ssize_t user_settings_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + unsigned long val; + int ret; + _mali_uk_user_setting_t setting; + char buf[32]; + + cnt = min(cnt, sizeof(buf) - 1); + if (copy_from_user(buf, ubuf, cnt)) { + return -EFAULT; + } + buf[cnt] = '\0'; + + ret = strict_strtoul(buf, 10, &val); + if (0 != ret) { + return ret; + } + + /* Update setting */ + setting = (_mali_uk_user_setting_t)(filp->private_data); + mali_set_user_setting(setting, val); + + *ppos += cnt; + return cnt; +} + +static ssize_t user_settings_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char buf[64]; + size_t r; + u32 value; + _mali_uk_user_setting_t setting; + + setting = (_mali_uk_user_setting_t)(filp->private_data); + value = mali_get_user_setting(setting); + + r = snprintf(buf, 64, "%u\n", value); + return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); +} + +static const struct file_operations user_settings_fops = { + .owner = THIS_MODULE, + .open = open_copy_private_data, + .read = user_settings_read, + .write = user_settings_write, +}; + +static int mali_sysfs_user_settings_register(void) +{ + struct dentry *mali_user_settings_dir = debugfs_create_dir("userspace_settings", mali_debugfs_dir); + + if (mali_user_settings_dir != NULL) { + int i; + for (i = 0; i < _MALI_UK_USER_SETTING_MAX; i++) { + debugfs_create_file(_mali_uk_user_setting_descriptions[i], 0600, mali_user_settings_dir, (void*)i, &user_settings_fops); + } + } + + return 0; +} + +static ssize_t pmu_power_down_write(struct file *filp, const char __user *buf, size_t count, loff_t *offp) +{ + int ret; + char buffer[32]; + unsigned long val; + struct mali_pmu_core *pmu; + _mali_osk_errcode_t err; + + if (count >= sizeof(buffer)) { + return -ENOMEM; + } + + if (copy_from_user(&buffer[0], buf, count)) { + return -EFAULT; + } + buffer[count] = '\0'; + + ret = strict_strtoul(&buffer[0], 10, &val); + if (0 != ret) { + return -EINVAL; + } + + pmu = mali_pmu_get_global_pmu_core(); + MALI_DEBUG_ASSERT_POINTER(pmu); + + err = mali_pmu_power_down(pmu, val); + if (_MALI_OSK_ERR_OK != err) { + return -EINVAL; + } + + *offp += count; + return count; +} + +static ssize_t pmu_power_up_write(struct file *filp, const char __user *buf, size_t count, loff_t *offp) +{ + int ret; + char buffer[32]; + unsigned long val; + struct mali_pmu_core *pmu; + _mali_osk_errcode_t err; + + if (count >= sizeof(buffer)) { + return -ENOMEM; + } + + if (copy_from_user(&buffer[0], buf, count)) { + return -EFAULT; + } + buffer[count] = '\0'; + + ret = strict_strtoul(&buffer[0], 10, &val); + if (0 != ret) { + return -EINVAL; + } + + pmu = mali_pmu_get_global_pmu_core(); + MALI_DEBUG_ASSERT_POINTER(pmu); + + err = mali_pmu_power_up(pmu, val); + if (_MALI_OSK_ERR_OK != err) { + return -EINVAL; + } + + *offp += count; + return count; +} + +static const struct file_operations pmu_power_down_fops = { + .owner = THIS_MODULE, + .write = pmu_power_down_write, +}; + +static const struct file_operations pmu_power_up_fops = { + .owner = THIS_MODULE, + .write = pmu_power_up_write, +}; + +static ssize_t pp_num_cores_enabled_write(struct file *filp, const char __user *buf, size_t count, loff_t *offp) +{ + int ret; + char buffer[32]; + unsigned long val; + + if (count >= sizeof(buffer)) { + return -ENOMEM; + } + + if (copy_from_user(&buffer[0], buf, count)) { + return -EFAULT; + } + buffer[count] = '\0'; + + ret = strict_strtoul(&buffer[0], 10, &val); + if (0 != ret) { + return -EINVAL; + } + + ret = mali_pp_scheduler_set_perf_level(val, MALI_TRUE); /* override even if core scaling is disabled */ + if (ret) { + return ret; + } + + *offp += count; + return count; +} + +static ssize_t pp_num_cores_enabled_read(struct file *filp, char __user *buf, size_t count, loff_t *offp) +{ + int r; + char buffer[64]; + + r = sprintf(buffer, "%u\n", mali_pp_scheduler_get_num_cores_enabled()); + + return simple_read_from_buffer(buf, count, offp, buffer, r); +} + +static const struct file_operations pp_num_cores_enabled_fops = { + .owner = THIS_MODULE, + .write = pp_num_cores_enabled_write, + .read = pp_num_cores_enabled_read, + .llseek = default_llseek, +}; + +static ssize_t pp_num_cores_total_read(struct file *filp, char __user *buf, size_t count, loff_t *offp) +{ + int r; + char buffer[64]; + + r = sprintf(buffer, "%u\n", mali_pp_scheduler_get_num_cores_total()); + + return simple_read_from_buffer(buf, count, offp, buffer, r); +} + +static const struct file_operations pp_num_cores_total_fops = { + .owner = THIS_MODULE, + .read = pp_num_cores_total_read, +}; + +static ssize_t pp_core_scaling_enabled_write(struct file *filp, const char __user *buf, size_t count, loff_t *offp) +{ + int ret; + char buffer[32]; + unsigned long val; + + if (count >= sizeof(buffer)) { + return -ENOMEM; + } + + if (copy_from_user(&buffer[0], buf, count)) { + return -EFAULT; + } + buffer[count] = '\0'; + + ret = strict_strtoul(&buffer[0], 10, &val); + if (0 != ret) { + return -EINVAL; + } + + switch (val) { + case 1: + mali_pp_scheduler_core_scaling_enable(); + break; + case 0: + mali_pp_scheduler_core_scaling_disable(); + break; + default: + return -EINVAL; + break; + } + + *offp += count; + return count; +} + +static ssize_t pp_core_scaling_enabled_read(struct file *filp, char __user *buf, size_t count, loff_t *offp) +{ + return simple_read_from_buffer(buf, count, offp, mali_pp_scheduler_core_scaling_is_enabled() ? "1\n" : "0\n", 2); +} +static const struct file_operations pp_core_scaling_enabled_fops = { + .owner = THIS_MODULE, + .write = pp_core_scaling_enabled_write, + .read = pp_core_scaling_enabled_read, + .llseek = default_llseek, +}; + +static ssize_t version_read(struct file *filp, char __user *buf, size_t count, loff_t *offp) +{ + int r = 0; + char buffer[64]; + + switch (mali_kernel_core_get_product_id()) { + case _MALI_PRODUCT_ID_MALI200: + r = sprintf(buffer, "Mali-200\n"); + break; + case _MALI_PRODUCT_ID_MALI300: + r = sprintf(buffer, "Mali-300\n"); + break; + case _MALI_PRODUCT_ID_MALI400: + r = sprintf(buffer, "Mali-400 MP\n"); + break; + case _MALI_PRODUCT_ID_MALI450: + r = sprintf(buffer, "Mali-450 MP\n"); + break; + case _MALI_PRODUCT_ID_UNKNOWN: + return -EINVAL; + break; + }; + + return simple_read_from_buffer(buf, count, offp, buffer, r); +} + +static const struct file_operations version_fops = { + .owner = THIS_MODULE, + .read = version_read, +}; + +int mali_sysfs_register(const char *mali_dev_name) +{ + mali_debugfs_dir = debugfs_create_dir(mali_dev_name, NULL); + if(ERR_PTR(-ENODEV) == mali_debugfs_dir) { + /* Debugfs not supported. */ + mali_debugfs_dir = NULL; + } else { + if(NULL != mali_debugfs_dir) { + /* Debugfs directory created successfully; create files now */ + struct dentry *mali_pmu_dir; + struct dentry *mali_power_dir; + struct dentry *mali_gp_dir; + struct dentry *mali_pp_dir; + struct dentry *mali_l2_dir; + struct dentry *mali_profiling_dir; + + debugfs_create_file("version", 0400, mali_debugfs_dir, NULL, &version_fops); + + mali_pmu_dir = debugfs_create_dir("pmu", mali_debugfs_dir); + if (NULL != mali_pmu_dir) { + debugfs_create_file("power_down", 0200, mali_pmu_dir, NULL, &pmu_power_down_fops); + debugfs_create_file("power_up", 0200, mali_pmu_dir, NULL, &pmu_power_up_fops); + } + + mali_power_dir = debugfs_create_dir("power", mali_debugfs_dir); + if (mali_power_dir != NULL) { + debugfs_create_file("always_on", 0600, mali_power_dir, NULL, &power_always_on_fops); + debugfs_create_file("power_events", 0200, mali_power_dir, NULL, &power_power_events_fops); + } + + mali_gp_dir = debugfs_create_dir("gp", mali_debugfs_dir); + if (mali_gp_dir != NULL) { + u32 num_groups; + int i; + + num_groups = mali_group_get_glob_num_groups(); + for (i = 0; i < num_groups; i++) { + struct mali_group *group = mali_group_get_glob_group(i); + + struct mali_gp_core *gp_core = mali_group_get_gp_core(group); + if (NULL != gp_core) { + struct dentry *mali_gp_gpx_dir; + mali_gp_gpx_dir = debugfs_create_dir("gp0", mali_gp_dir); + if (NULL != mali_gp_gpx_dir) { + debugfs_create_file("base_addr", 0400, mali_gp_gpx_dir, &gp_core->hw_core, &hw_core_base_addr_fops); + debugfs_create_file("enabled", 0600, mali_gp_gpx_dir, group, &group_enabled_fops); + } + break; /* no need to look for any other GP cores */ + } + + } + } + + mali_pp_dir = debugfs_create_dir("pp", mali_debugfs_dir); + if (mali_pp_dir != NULL) { + u32 num_groups; + int i; + + debugfs_create_file("num_cores_total", 0400, mali_pp_dir, NULL, &pp_num_cores_total_fops); + debugfs_create_file("num_cores_enabled", 0600, mali_pp_dir, NULL, &pp_num_cores_enabled_fops); + debugfs_create_file("core_scaling_enabled", 0600, mali_pp_dir, NULL, &pp_core_scaling_enabled_fops); + + num_groups = mali_group_get_glob_num_groups(); + for (i = 0; i < num_groups; i++) { + struct mali_group *group = mali_group_get_glob_group(i); + + struct mali_pp_core *pp_core = mali_group_get_pp_core(group); + if (NULL != pp_core) { + char buf[16]; + struct dentry *mali_pp_ppx_dir; + _mali_osk_snprintf(buf, sizeof(buf), "pp%u", mali_pp_core_get_id(pp_core)); + mali_pp_ppx_dir = debugfs_create_dir(buf, mali_pp_dir); + if (NULL != mali_pp_ppx_dir) { + debugfs_create_file("base_addr", 0400, mali_pp_ppx_dir, &pp_core->hw_core, &hw_core_base_addr_fops); + if (!mali_group_is_virtual(group)) { + debugfs_create_file("enabled", 0600, mali_pp_ppx_dir, group, &group_enabled_fops); + } + } + } + } + } + + mali_l2_dir = debugfs_create_dir("l2", mali_debugfs_dir); + if (mali_l2_dir != NULL) { + struct dentry *mali_l2_all_dir; + u32 l2_id; + struct mali_l2_cache_core *l2_cache; + + mali_l2_all_dir = debugfs_create_dir("all", mali_l2_dir); + if (mali_l2_all_dir != NULL) { + debugfs_create_file("counter_src0", 0200, mali_l2_all_dir, NULL, &l2_all_counter_src0_fops); + debugfs_create_file("counter_src1", 0200, mali_l2_all_dir, NULL, &l2_all_counter_src1_fops); + } + + l2_id = 0; + l2_cache = mali_l2_cache_core_get_glob_l2_core(l2_id); + while (NULL != l2_cache) { + char buf[16]; + struct dentry *mali_l2_l2x_dir; + _mali_osk_snprintf(buf, sizeof(buf), "l2%u", l2_id); + mali_l2_l2x_dir = debugfs_create_dir(buf, mali_l2_dir); + if (NULL != mali_l2_l2x_dir) { + debugfs_create_file("counter_src0", 0600, mali_l2_l2x_dir, l2_cache, &l2_l2x_counter_src0_fops); + debugfs_create_file("counter_src1", 0600, mali_l2_l2x_dir, l2_cache, &l2_l2x_counter_src1_fops); + debugfs_create_file("base_addr", 0400, mali_l2_l2x_dir, &l2_cache->hw_core, &hw_core_base_addr_fops); + } + + /* try next L2 */ + l2_id++; + l2_cache = mali_l2_cache_core_get_glob_l2_core(l2_id); + } + } + + debugfs_create_file("memory_usage", 0400, mali_debugfs_dir, NULL, &memory_usage_fops); + + debugfs_create_file("utilization_gp_pp", 0400, mali_debugfs_dir, NULL, &utilization_gp_pp_fops); + debugfs_create_file("utilization_gp", 0400, mali_debugfs_dir, NULL, &utilization_gp_fops); + debugfs_create_file("utilization_pp", 0400, mali_debugfs_dir, NULL, &utilization_pp_fops); + + mali_profiling_dir = debugfs_create_dir("profiling", mali_debugfs_dir); + if (mali_profiling_dir != NULL) { + u32 max_sub_jobs; + int i; + struct dentry *mali_profiling_gp_dir; + struct dentry *mali_profiling_pp_dir; +#if defined(CONFIG_MALI400_INTERNAL_PROFILING) + struct dentry *mali_profiling_proc_dir; +#endif + /* + * Create directory where we can set GP HW counters. + */ + mali_profiling_gp_dir = debugfs_create_dir("gp", mali_profiling_dir); + if (mali_profiling_gp_dir != NULL) { + debugfs_create_file("counter_src0", 0600, mali_profiling_gp_dir, (void*)PRIVATE_DATA_COUNTER_MAKE_GP(0), &profiling_counter_src_fops); + debugfs_create_file("counter_src1", 0600, mali_profiling_gp_dir, (void*)PRIVATE_DATA_COUNTER_MAKE_GP(1), &profiling_counter_src_fops); + } + + /* + * Create directory where we can set PP HW counters. + * Possible override with specific HW counters for a particular sub job + * (Disable core scaling before using the override!) + */ + mali_profiling_pp_dir = debugfs_create_dir("pp", mali_profiling_dir); + if (mali_profiling_pp_dir != NULL) { + debugfs_create_file("counter_src0", 0600, mali_profiling_pp_dir, (void*)PRIVATE_DATA_COUNTER_MAKE_PP(0), &profiling_counter_src_fops); + debugfs_create_file("counter_src1", 0600, mali_profiling_pp_dir, (void*)PRIVATE_DATA_COUNTER_MAKE_PP(1), &profiling_counter_src_fops); + } + + max_sub_jobs = mali_pp_scheduler_get_num_cores_total(); + for (i = 0; i < max_sub_jobs; i++) { + char buf[16]; + struct dentry *mali_profiling_pp_x_dir; + _mali_osk_snprintf(buf, sizeof(buf), "%u", i); + mali_profiling_pp_x_dir = debugfs_create_dir(buf, mali_profiling_pp_dir); + if (NULL != mali_profiling_pp_x_dir) { + debugfs_create_file("counter_src0", 0600, mali_profiling_pp_x_dir, (void*)PRIVATE_DATA_COUNTER_MAKE_PP_SUB_JOB(0, i), &profiling_counter_src_fops); + debugfs_create_file("counter_src1", 0600, mali_profiling_pp_x_dir, (void*)PRIVATE_DATA_COUNTER_MAKE_PP_SUB_JOB(1, i), &profiling_counter_src_fops); + } + } + +#if defined(CONFIG_MALI400_INTERNAL_PROFILING) + mali_profiling_proc_dir = debugfs_create_dir("proc", mali_profiling_dir); + if (mali_profiling_proc_dir != NULL) { + struct dentry *mali_profiling_proc_default_dir = debugfs_create_dir("default", mali_profiling_proc_dir); + if (mali_profiling_proc_default_dir != NULL) { + debugfs_create_file("enable", 0600, mali_profiling_proc_default_dir, (void*)_MALI_UK_USER_SETTING_SW_EVENTS_ENABLE, &user_settings_fops); + } + } + debugfs_create_file("record", 0600, mali_profiling_dir, NULL, &profiling_record_fops); + debugfs_create_file("events", 0400, mali_profiling_dir, NULL, &profiling_events_fops); + debugfs_create_file("events_human_readable", 0400, mali_profiling_dir, NULL, &profiling_events_human_readable_fops); +#endif + } + +#if MALI_STATE_TRACKING + debugfs_create_file("state_dump", 0400, mali_debugfs_dir, NULL, &mali_seq_internal_state_fops); +#endif + + if (mali_sysfs_user_settings_register()) { + /* Failed to create the debugfs entries for the user settings DB. */ + MALI_DEBUG_PRINT(2, ("Failed to create user setting debugfs files. Ignoring...\n")); + } + } + } + + /* Success! */ + return 0; +} + +int mali_sysfs_unregister(void) +{ + if(NULL != mali_debugfs_dir) { + debugfs_remove_recursive(mali_debugfs_dir); + } + return 0; +} + +#else /* MALI_LICENSE_IS_GPL */ + +/* Dummy implementations for non-GPL */ + +int mali_sysfs_register(struct mali_dev *device, dev_t dev, const char *mali_dev_name) +{ + return 0; +} + +int mali_sysfs_unregister(void) +{ + return 0; +} + +#endif /* MALI_LICENSE_IS_GPL */ diff --git a/drivers/gpu/arm/mali/linux/mali_kernel_sysfs.h b/drivers/gpu/arm/mali/linux/mali_kernel_sysfs.h new file mode 100644 index 00000000000000..af1ddde9a5969f --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_kernel_sysfs.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2011-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_SYSFS_H__ +#define __MALI_KERNEL_SYSFS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define MALI_PROC_DIR "driver/mali" + +int mali_sysfs_register(const char *mali_dev_name); +int mali_sysfs_unregister(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_KERNEL_LINUX_H__ */ diff --git a/drivers/gpu/arm/mali/linux/mali_linux_trace.h b/drivers/gpu/arm/mali/linux/mali_linux_trace.h new file mode 100644 index 00000000000000..6c20250c1e8d3b --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_linux_trace.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#if !defined (MALI_LINUX_TRACE_H) || defined (TRACE_HEADER_MULTI_READ) +#define MALI_LINUX_TRACE_H + +#include + +#include +#include + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM mali +#define TRACE_SYSTEM_STRING __stringfy(TRACE_SYSTEM) + +#define TRACE_INCLUDE_PATH . +#define TRACE_INCLUDE_FILE mali_linux_trace + +/** + * Define the tracepoint used to communicate the status of a GPU. Called + * when a GPU turns on or turns off. + * + * @param event_id The type of the event. This parameter is a bitfield + * encoding the type of the event. + * + * @param d0 First data parameter. + * @param d1 Second data parameter. + * @param d2 Third data parameter. + * @param d3 Fourth data parameter. + * @param d4 Fifth data parameter. + */ +TRACE_EVENT(mali_timeline_event, + + TP_PROTO(unsigned int event_id, unsigned int d0, unsigned int d1, + unsigned int d2, unsigned int d3, unsigned int d4), + + TP_ARGS(event_id, d0, d1, d2, d3, d4), + + TP_STRUCT__entry( + __field(unsigned int, event_id) + __field(unsigned int, d0) + __field(unsigned int, d1) + __field(unsigned int, d2) + __field(unsigned int, d3) + __field(unsigned int, d4) + ), + + TP_fast_assign( + __entry->event_id = event_id; + __entry->d0 = d0; + __entry->d1 = d1; + __entry->d2 = d2; + __entry->d3 = d3; + __entry->d4 = d4; + ), + + TP_printk("event=%d", __entry->event_id) + ); + +/** + * Define a tracepoint used to regsiter the value of a hardware counter. + * Hardware counters belonging to the vertex or fragment processor are + * reported via this tracepoint each frame, whilst L2 cache hardware + * counters are reported continuously. + * + * @param counter_id The counter ID. + * @param value The value of the counter. + */ +TRACE_EVENT(mali_hw_counter, + + TP_PROTO(unsigned int counter_id, unsigned int value), + + TP_ARGS(counter_id, value), + + TP_STRUCT__entry( + __field(unsigned int, counter_id) + __field(unsigned int, value) + ), + + TP_fast_assign( + __entry->counter_id = counter_id; + ), + + TP_printk("event %d = %d", __entry->counter_id, __entry->value) + ); + +/** + * Define a tracepoint used to send a bundle of software counters. + * + * @param counters The bundle of counters. + */ +TRACE_EVENT(mali_sw_counters, + + TP_PROTO(pid_t pid, pid_t tid, void * surface_id, unsigned int * counters), + + TP_ARGS(pid, tid, surface_id, counters), + + TP_STRUCT__entry( + __field(pid_t, pid) + __field(pid_t, tid) + __field(void *, surface_id) + __field(unsigned int *, counters) + ), + + TP_fast_assign( + __entry->pid = pid; + __entry->tid = tid; + __entry->surface_id = surface_id; + __entry->counters = counters; + ), + + TP_printk("counters were %s", __entry->counters == NULL? "NULL" : "not NULL") + ); + +#endif /* MALI_LINUX_TRACE_H */ + +/* This part must exist outside the header guard. */ +#include + diff --git a/drivers/gpu/arm/mali/linux/mali_memory.c b/drivers/gpu/arm/mali/linux/mali_memory.c new file mode 100644 index 00000000000000..f0c465806c7ae8 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_memory.c @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mali_osk.h" +#include "mali_osk_mali.h" +#include "mali_kernel_linux.h" +#include "mali_scheduler.h" +#include "mali_kernel_descriptor_mapping.h" + +#include "mali_memory.h" +#include "mali_memory_dma_buf.h" +#include "mali_memory_os_alloc.h" +#include "mali_memory_block_alloc.h" + +/* session->memory_lock must be held when calling this function */ +static void mali_mem_release(mali_mem_allocation *descriptor) +{ + MALI_DEBUG_ASSERT_POINTER(descriptor); + MALI_DEBUG_ASSERT_LOCK_HELD(descriptor->session->memory_lock); + + MALI_DEBUG_ASSERT(MALI_MEM_ALLOCATION_VALID_MAGIC == descriptor->magic); + + switch (descriptor->type) { + case MALI_MEM_OS: + mali_mem_os_release(descriptor); + break; + case MALI_MEM_DMA_BUF: +#if defined(CONFIG_DMA_SHARED_BUFFER) + mali_mem_dma_buf_release(descriptor); +#endif + break; + case MALI_MEM_UMP: +#if defined(CONFIG_MALI400_UMP) + mali_mem_ump_release(descriptor); +#endif + break; + case MALI_MEM_EXTERNAL: + mali_mem_external_release(descriptor); + break; + case MALI_MEM_BLOCK: + mali_mem_block_release(descriptor); + break; + } +} + +static void mali_mem_vma_open(struct vm_area_struct * vma) +{ + mali_mem_allocation *descriptor = (mali_mem_allocation*)vma->vm_private_data; + MALI_DEBUG_PRINT(4, ("Open called on vma %p\n", vma)); + + descriptor->cpu_mapping.ref++; + + return; +} + +static void mali_mem_vma_close(struct vm_area_struct *vma) +{ + mali_mem_allocation *descriptor; + struct mali_session_data *session; + mali_mem_virt_cpu_mapping *mapping; + + MALI_DEBUG_PRINT(3, ("Close called on vma %p\n", vma)); + + descriptor = (mali_mem_allocation*)vma->vm_private_data; + BUG_ON(!descriptor); + + MALI_DEBUG_ASSERT(MALI_MEM_ALLOCATION_VALID_MAGIC == descriptor->magic); + + mapping = &descriptor->cpu_mapping; + BUG_ON(0 == mapping->ref); + + mapping->ref--; + if (0 != mapping->ref) { + MALI_DEBUG_PRINT(3, ("Ignoring this close, %d references still exists\n", mapping->ref)); + return; + } + + session = descriptor->session; + + mali_descriptor_mapping_free(session->descriptor_mapping, descriptor->id); + + _mali_osk_mutex_wait(session->memory_lock); + mali_mem_release(descriptor); + _mali_osk_mutex_signal(session->memory_lock); + + mali_mem_descriptor_destroy(descriptor); +} + +static int mali_kernel_memory_cpu_page_fault_handler(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + void __user * address; + mali_mem_allocation *descriptor; + + address = vmf->virtual_address; + descriptor = (mali_mem_allocation *)vma->vm_private_data; + + MALI_DEBUG_ASSERT(MALI_MEM_ALLOCATION_VALID_MAGIC == descriptor->magic); + + /* + * We always fail the call since all memory is pre-faulted when assigned to the process. + * Only the Mali cores can use page faults to extend buffers. + */ + + MALI_DEBUG_PRINT(1, ("Page-fault in Mali memory region caused by the CPU.\n")); + MALI_DEBUG_PRINT(1, ("Tried to access %p (process local virtual address) which is not currently mapped to any Mali memory.\n", (void*)address)); + + MALI_IGNORE(address); + MALI_IGNORE(descriptor); + + return VM_FAULT_SIGBUS; +} + +struct vm_operations_struct mali_kernel_vm_ops = { + .open = mali_mem_vma_open, + .close = mali_mem_vma_close, + .fault = mali_kernel_memory_cpu_page_fault_handler +}; + +/** @note munmap handler is done by vma close handler */ +int mali_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct mali_session_data *session; + mali_mem_allocation *descriptor; + u32 size = vma->vm_end - vma->vm_start; + u32 mali_addr = vma->vm_pgoff << PAGE_SHIFT; + + session = (struct mali_session_data *)filp->private_data; + if (NULL == session) { + MALI_PRINT_ERROR(("mmap called without any session data available\n")); + return -EFAULT; + } + + MALI_DEBUG_PRINT(4, ("MMap() handler: start=0x%08X, phys=0x%08X, size=0x%08X vma->flags 0x%08x\n", + (unsigned int)vma->vm_start, (unsigned int)(vma->vm_pgoff << PAGE_SHIFT), + (unsigned int)(vma->vm_end - vma->vm_start), vma->vm_flags)); + + /* Set some bits which indicate that, the memory is IO memory, meaning + * that no paging is to be performed and the memory should not be + * included in crash dumps. And that the memory is reserved, meaning + * that it's present and can never be paged out (see also previous + * entry) + */ + vma->vm_flags |= VM_IO; + vma->vm_flags |= VM_DONTCOPY; + vma->vm_flags |= VM_PFNMAP; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) + vma->vm_flags |= VM_RESERVED; +#else + vma->vm_flags |= VM_DONTDUMP; + vma->vm_flags |= VM_DONTEXPAND; +#endif + + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + vma->vm_ops = &mali_kernel_vm_ops; /* Operations used on any memory system */ + + descriptor = mali_mem_block_alloc(mali_addr, size, vma, session); + if (NULL == descriptor) { + descriptor = mali_mem_os_alloc(mali_addr, size, vma, session); + if (NULL == descriptor) { + MALI_DEBUG_PRINT(3, ("MMAP failed\n")); + return -ENOMEM; + } + } + + MALI_DEBUG_ASSERT(MALI_MEM_ALLOCATION_VALID_MAGIC == descriptor->magic); + + vma->vm_private_data = (void*)descriptor; + + /* Put on descriptor map */ + if (_MALI_OSK_ERR_OK != mali_descriptor_mapping_allocate_mapping(session->descriptor_mapping, descriptor, &descriptor->id)) { + _mali_osk_mutex_wait(session->memory_lock); + mali_mem_os_release(descriptor); + _mali_osk_mutex_signal(session->memory_lock); + return -EFAULT; + } + + return 0; +} + + +/* Prepare memory descriptor */ +mali_mem_allocation *mali_mem_descriptor_create(struct mali_session_data *session, mali_mem_type type) +{ + mali_mem_allocation *descriptor; + + descriptor = (mali_mem_allocation*)kzalloc(sizeof(mali_mem_allocation), GFP_KERNEL); + if (NULL == descriptor) { + MALI_DEBUG_PRINT(3,("mali_ukk_mem_mmap: descriptor was NULL\n")); + return NULL; + } + + MALI_DEBUG_CODE(descriptor->magic = MALI_MEM_ALLOCATION_VALID_MAGIC); + + descriptor->flags = 0; + descriptor->type = type; + descriptor->session = session; + + return descriptor; +} + +void mali_mem_descriptor_destroy(mali_mem_allocation *descriptor) +{ + MALI_DEBUG_ASSERT(MALI_MEM_ALLOCATION_VALID_MAGIC == descriptor->magic); + MALI_DEBUG_CODE(descriptor->magic = MALI_MEM_ALLOCATION_FREED_MAGIC); + + kfree(descriptor); +} + +_mali_osk_errcode_t mali_mem_mali_map_prepare(mali_mem_allocation *descriptor) +{ + u32 size = descriptor->size; + struct mali_session_data *session = descriptor->session; + + MALI_DEBUG_ASSERT(MALI_MEM_ALLOCATION_VALID_MAGIC == descriptor->magic); + + /* Map dma-buf into this session's page tables */ + + if (descriptor->flags & MALI_MEM_FLAG_MALI_GUARD_PAGE) { + size += MALI_MMU_PAGE_SIZE; + } + + return mali_mmu_pagedir_map(session->page_directory, descriptor->mali_mapping.addr, size); +} + +void mali_mem_mali_map_free(mali_mem_allocation *descriptor) +{ + u32 size = descriptor->size; + struct mali_session_data *session = descriptor->session; + + MALI_DEBUG_ASSERT(MALI_MEM_ALLOCATION_VALID_MAGIC == descriptor->magic); + + if (descriptor->flags & MALI_MEM_FLAG_MALI_GUARD_PAGE) { + size += MALI_MMU_PAGE_SIZE; + } + + /* Umap and flush L2 */ + mali_mmu_pagedir_unmap(session->page_directory, descriptor->mali_mapping.addr, descriptor->size); + + mali_scheduler_zap_all_active(session); +} + +u32 _mali_ukk_report_memory_usage(void) +{ + u32 sum = 0; + + sum += mali_mem_block_allocator_stat(); + sum += mali_mem_os_stat(); + + return sum; +} + +/** + * Per-session memory descriptor mapping table sizes + */ +#define MALI_MEM_DESCRIPTORS_INIT 64 +#define MALI_MEM_DESCRIPTORS_MAX 65536 + +_mali_osk_errcode_t mali_memory_session_begin(struct mali_session_data * session_data) +{ + MALI_DEBUG_PRINT(5, ("Memory session begin\n")); + + /* Create descriptor mapping table */ + session_data->descriptor_mapping = mali_descriptor_mapping_create(MALI_MEM_DESCRIPTORS_INIT, MALI_MEM_DESCRIPTORS_MAX); + + if (NULL == session_data->descriptor_mapping) { + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + session_data->memory_lock = _mali_osk_mutex_init(_MALI_OSK_LOCKFLAG_ORDERED, + _MALI_OSK_LOCK_ORDER_MEM_SESSION); + + if (NULL == session_data->memory_lock) { + mali_descriptor_mapping_destroy(session_data->descriptor_mapping); + _mali_osk_free(session_data); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + MALI_DEBUG_PRINT(5, ("MMU session begin: success\n")); + MALI_SUCCESS; +} + +/** @brief Callback function that releases memory + * + * session->memory_lock must be held when calling this function. + */ +static void descriptor_table_cleanup_callback(int descriptor_id, void* map_target) +{ + mali_mem_allocation *descriptor; + + descriptor = (mali_mem_allocation*)map_target; + + MALI_DEBUG_ASSERT_LOCK_HELD(descriptor->session->memory_lock); + + MALI_DEBUG_PRINT(3, ("Cleanup of descriptor %d mapping to 0x%x in descriptor table\n", descriptor_id, map_target)); + MALI_DEBUG_ASSERT(descriptor); + + mali_mem_release(descriptor); + mali_mem_descriptor_destroy(descriptor); +} + +void mali_memory_session_end(struct mali_session_data *session) +{ + MALI_DEBUG_PRINT(3, ("MMU session end\n")); + + if (NULL == session) { + MALI_DEBUG_PRINT(1, ("No session data found during session end\n")); + return; + } + + /* Lock the session so we can modify the memory list */ + _mali_osk_mutex_wait(session->memory_lock); + + /* Free all allocations still in the descriptor map, and terminate the map */ + if (NULL != session->descriptor_mapping) { + mali_descriptor_mapping_call_for_each(session->descriptor_mapping, descriptor_table_cleanup_callback); + mali_descriptor_mapping_destroy(session->descriptor_mapping); + session->descriptor_mapping = NULL; + } + + _mali_osk_mutex_signal(session->memory_lock); + + /* Free the lock */ + _mali_osk_mutex_term(session->memory_lock); + + return; +} + +_mali_osk_errcode_t mali_memory_initialize(void) +{ + return mali_mem_os_init(); +} + +void mali_memory_terminate(void) +{ + mali_mem_os_term(); + mali_mem_block_allocator_destroy(NULL); +} diff --git a/drivers/gpu/arm/mali/linux/mali_memory.h b/drivers/gpu/arm/mali/linux/mali_memory.h new file mode 100644 index 00000000000000..a5a02167426d27 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_memory.h @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_MEMORY_H__ +#define __MALI_MEMORY_H__ + +#include "mali_osk.h" +#include "mali_session.h" + +#include +#include + +#include "mali_memory_types.h" +#include "mali_memory_os_alloc.h" + +_mali_osk_errcode_t mali_memory_initialize(void); +void mali_memory_terminate(void); + +/** @brief Allocate a page table page + * + * Allocate a page for use as a page directory or page table. The page is + * mapped into kernel space. + * + * @return _MALI_OSK_ERR_OK on success, otherwise an error code + * @param table_page GPU pointer to the allocated page + * @param mapping CPU pointer to the mapping of the allocated page + */ +MALI_STATIC_INLINE _mali_osk_errcode_t mali_mmu_get_table_page(u32 *table_page, mali_io_address *mapping) +{ + return mali_mem_os_get_table_page(table_page, mapping); +} + +/** @brief Release a page table page + * + * Release a page table page allocated through \a mali_mmu_get_table_page + * + * @param pa the GPU address of the page to release + */ +MALI_STATIC_INLINE void mali_mmu_release_table_page(u32 phys, void *virt) +{ + mali_mem_os_release_table_page(phys, virt); +} + +/** @brief mmap function + * + * mmap syscalls on the Mali device node will end up here. + * + * This function allocates Mali memory and maps it on CPU and Mali. + */ +int mali_mmap(struct file *filp, struct vm_area_struct *vma); + +/** @brief Allocate and initialize a Mali memory descriptor + * + * @param session Pointer to the session allocating the descriptor + * @param type Type of memory the descriptor will represent + */ +mali_mem_allocation *mali_mem_descriptor_create(struct mali_session_data *session, mali_mem_type type); + +/** @brief Destroy a Mali memory descriptor + * + * This function will only free the descriptor itself, and not the memory it + * represents. + * + * @param descriptor Pointer to the descriptor to destroy + */ +void mali_mem_descriptor_destroy(mali_mem_allocation *descriptor); + +/** @brief Start a new memory session + * + * Called when a process opens the Mali device node. + * + * @param session Pointer to session to initialize + */ +_mali_osk_errcode_t mali_memory_session_begin(struct mali_session_data *session); + +/** @brief Close a memory session + * + * Called when a process closes the Mali device node. + * + * Memory allocated by the session will be freed + * + * @param session Pointer to the session to terminate + */ +void mali_memory_session_end(struct mali_session_data *session); + +/** @brief Prepare Mali page tables for mapping + * + * This function will prepare the Mali page tables for mapping the memory + * described by \a descriptor. + * + * Page tables will be reference counted and allocated, if not yet present. + * + * @param descriptor Pointer to the memory descriptor to the mapping + */ +_mali_osk_errcode_t mali_mem_mali_map_prepare(mali_mem_allocation *descriptor); + +/** @brief Free Mali page tables for mapping + * + * This function will unmap pages from Mali memory and free the page tables + * that are now unused. + * + * The updated pages in the Mali L2 cache will be invalidated, and the MMU TLBs will be zapped if necessary. + * + * @param descriptor Pointer to the memory descriptor to unmap + */ +void mali_mem_mali_map_free(mali_mem_allocation *descriptor); + +/** @brief Parse resource and prepare the OS memory allocator + * + * @param size Maximum size to allocate for Mali GPU. + * @return _MALI_OSK_ERR_OK on success, otherwise failure. + */ +_mali_osk_errcode_t mali_memory_core_resource_os_memory(u32 size); + +/** @brief Parse resource and prepare the dedicated memory allocator + * + * @param start Physical start address of dedicated Mali GPU memory. + * @param size Size of dedicated Mali GPU memory. + * @return _MALI_OSK_ERR_OK on success, otherwise failure. + */ +_mali_osk_errcode_t mali_memory_core_resource_dedicated_memory(u32 start, u32 size); + + +void mali_mem_ump_release(mali_mem_allocation *descriptor); +void mali_mem_external_release(mali_mem_allocation *descriptor); + +#endif /* __MALI_MEMORY_H__ */ diff --git a/drivers/gpu/arm/mali/linux/mali_memory_block_alloc.c b/drivers/gpu/arm/mali/linux/mali_memory_block_alloc.c new file mode 100644 index 00000000000000..2847893dcd2d40 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_memory_block_alloc.c @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "mali_kernel_common.h" +#include "mali_memory.h" +#include "mali_memory_block_alloc.h" +#include "mali_osk.h" +#include +#define MALI_BLOCK_SIZE (256UL * 1024UL) /* 256 kB, remember to keep the ()s */ + +struct block_info { + struct block_info *next; +}; + +typedef struct block_info block_info; + + +typedef struct block_allocator { + struct mutex mutex; + block_info *all_blocks; + block_info *first_free; + u32 base; + u32 cpu_usage_adjust; + u32 num_blocks; + u32 free_blocks; +} block_allocator; + +static block_allocator *mali_mem_block_gobal_allocator = NULL; + +MALI_STATIC_INLINE u32 get_phys(block_allocator *info, block_info *block) +{ + return info->base + ((block - info->all_blocks) * MALI_BLOCK_SIZE); +} + +mali_mem_allocator *mali_mem_block_allocator_create(u32 base_address, u32 cpu_usage_adjust, u32 size) +{ + block_allocator *info; + u32 usable_size; + u32 num_blocks; + + usable_size = size & ~(MALI_BLOCK_SIZE - 1); + MALI_DEBUG_PRINT(3, ("Mali block allocator create for region starting at 0x%08X length 0x%08X\n", base_address, size)); + MALI_DEBUG_PRINT(4, ("%d usable bytes\n", usable_size)); + num_blocks = usable_size / MALI_BLOCK_SIZE; + MALI_DEBUG_PRINT(4, ("which becomes %d blocks\n", num_blocks)); + + if (usable_size == 0) { + MALI_DEBUG_PRINT(1, ("Memory block of size %d is unusable\n", size)); + return NULL; + } + + info = _mali_osk_malloc(sizeof(block_allocator)); + if (NULL != info) { + mutex_init(&info->mutex); + info->all_blocks = _mali_osk_malloc(sizeof(block_info) * num_blocks); + if (NULL != info->all_blocks) { + u32 i; + info->first_free = NULL; + info->num_blocks = num_blocks; + info->free_blocks = num_blocks; + + info->base = base_address; + info->cpu_usage_adjust = cpu_usage_adjust; + + for ( i = 0; i < num_blocks; i++) { + info->all_blocks[i].next = info->first_free; + info->first_free = &info->all_blocks[i]; + } + + return (mali_mem_allocator *)info; + } + _mali_osk_free(info); + } + + return NULL; +} + +void mali_mem_block_allocator_destroy(mali_mem_allocator *allocator) +{ + block_allocator *info = (block_allocator*)allocator; + + info = mali_mem_block_gobal_allocator; + if (NULL == info) return; + + MALI_DEBUG_ASSERT_POINTER(info); + + _mali_osk_free(info->all_blocks); + _mali_osk_free(info); +} + +static void mali_mem_block_mali_map(mali_mem_allocation *descriptor, u32 phys, u32 virt, u32 size) +{ + struct mali_page_directory *pagedir = descriptor->session->page_directory; + u32 prop = descriptor->mali_mapping.properties; + u32 offset = 0; + + while (size) { + mali_mmu_pagedir_update(pagedir, virt + offset, phys + offset, MALI_MMU_PAGE_SIZE, prop); + + size -= MALI_MMU_PAGE_SIZE; + offset += MALI_MMU_PAGE_SIZE; + } +} + +static int mali_mem_block_cpu_map(mali_mem_allocation *descriptor, struct vm_area_struct *vma, u32 mali_phys, u32 mapping_offset, u32 size, u32 cpu_usage_adjust) +{ + u32 virt = vma->vm_start + mapping_offset; + u32 cpu_phys = mali_phys + cpu_usage_adjust; + u32 offset = 0; + int ret; + + while (size) { + ret = vm_insert_pfn(vma, virt + offset, __phys_to_pfn(cpu_phys + offset)); + + if (unlikely(ret)) { + MALI_DEBUG_PRINT(1, ("Block allocator: Failed to insert pfn into vma\n")); + return 1; + } + + size -= MALI_MMU_PAGE_SIZE; + offset += MALI_MMU_PAGE_SIZE; + } + + return 0; +} + +mali_mem_allocation *mali_mem_block_alloc(u32 mali_addr, u32 size, struct vm_area_struct *vma, struct mali_session_data *session) +{ + _mali_osk_errcode_t err; + mali_mem_allocation *descriptor; + block_allocator *info; + u32 left; + block_info *last_allocated = NULL; + block_allocator_allocation *ret_allocation; + u32 offset = 0; + + size = ALIGN(size, MALI_BLOCK_SIZE); + + info = mali_mem_block_gobal_allocator; + if (NULL == info) return NULL; + + left = size; + MALI_DEBUG_ASSERT(0 != left); + + descriptor = mali_mem_descriptor_create(session, MALI_MEM_BLOCK); + if (NULL == descriptor) { + return NULL; + } + + descriptor->mali_mapping.addr = mali_addr; + descriptor->size = size; + descriptor->cpu_mapping.addr = (void __user*)vma->vm_start; + descriptor->cpu_mapping.ref = 1; + + if (VM_SHARED == (VM_SHARED & vma->vm_flags)) { + descriptor->mali_mapping.properties = MALI_MMU_FLAGS_DEFAULT; + } else { + /* Cached Mali memory mapping */ + descriptor->mali_mapping.properties = MALI_MMU_FLAGS_FORCE_GP_READ_ALLOCATE; + vma->vm_flags |= VM_SHARED; + } + + ret_allocation = &descriptor->block_mem.mem; + + ret_allocation->mapping_length = 0; + + _mali_osk_mutex_wait(session->memory_lock); + mutex_lock(&info->mutex); + + if (left > (info->free_blocks * MALI_BLOCK_SIZE)) { + MALI_DEBUG_PRINT(2, ("Mali block allocator: not enough free blocks to service allocation (%u)\n", left)); + mutex_unlock(&info->mutex); + _mali_osk_mutex_signal(session->memory_lock); + mali_mem_descriptor_destroy(descriptor); + return NULL; + } + + err = mali_mem_mali_map_prepare(descriptor); + if (_MALI_OSK_ERR_OK != err) { + mutex_unlock(&info->mutex); + _mali_osk_mutex_signal(session->memory_lock); + mali_mem_descriptor_destroy(descriptor); + return NULL; + } + + while ((left > 0) && (info->first_free)) { + block_info *block; + u32 phys_addr; + u32 current_mapping_size; + + block = info->first_free; + info->first_free = info->first_free->next; + block->next = last_allocated; + last_allocated = block; + + phys_addr = get_phys(info, block); + + if (MALI_BLOCK_SIZE < left) { + current_mapping_size = MALI_BLOCK_SIZE; + } else { + current_mapping_size = left; + } + + mali_mem_block_mali_map(descriptor, phys_addr, mali_addr + offset, current_mapping_size); + if (mali_mem_block_cpu_map(descriptor, vma, phys_addr, offset, current_mapping_size, info->cpu_usage_adjust)) { + /* release all memory back to the pool */ + while (last_allocated) { + /* This relinks every block we've just allocated back into the free-list */ + block = last_allocated->next; + last_allocated->next = info->first_free; + info->first_free = last_allocated; + last_allocated = block; + } + + mutex_unlock(&info->mutex); + _mali_osk_mutex_signal(session->memory_lock); + + mali_mem_mali_map_free(descriptor); + mali_mem_descriptor_destroy(descriptor); + + return NULL; + } + + left -= current_mapping_size; + offset += current_mapping_size; + ret_allocation->mapping_length += current_mapping_size; + + --info->free_blocks; + } + + mutex_unlock(&info->mutex); + _mali_osk_mutex_signal(session->memory_lock); + + MALI_DEBUG_ASSERT(0 == left); + + /* Record all the information about this allocation */ + ret_allocation->last_allocated = last_allocated; + ret_allocation->info = info; + + return descriptor; +} + +void mali_mem_block_release(mali_mem_allocation *descriptor) +{ + block_allocator *info = descriptor->block_mem.mem.info; + block_info *block, *next; + block_allocator_allocation *allocation = &descriptor->block_mem.mem; + + MALI_DEBUG_ASSERT(MALI_MEM_BLOCK == descriptor->type); + + block = allocation->last_allocated; + + MALI_DEBUG_ASSERT_POINTER(block); + + /* unmap */ + mali_mem_mali_map_free(descriptor); + + mutex_lock(&info->mutex); + + while (block) { + MALI_DEBUG_ASSERT(!((block < info->all_blocks) || (block > (info->all_blocks + info->num_blocks)))); + + next = block->next; + + /* relink into free-list */ + block->next = info->first_free; + info->first_free = block; + + /* advance the loop */ + block = next; + + ++info->free_blocks; + } + + mutex_unlock(&info->mutex); +} + +u32 mali_mem_block_allocator_stat(void) +{ + block_allocator *info = (block_allocator *)mali_mem_block_gobal_allocator; + + if (NULL == info) return 0; + + MALI_DEBUG_ASSERT_POINTER(info); + + return (info->num_blocks - info->free_blocks) * MALI_BLOCK_SIZE; +} + +_mali_osk_errcode_t mali_memory_core_resource_dedicated_memory(u32 start, u32 size) +{ + mali_mem_allocator *allocator; + + /* Do the low level linux operation first */ + + /* Request ownership of the memory */ + if (_MALI_OSK_ERR_OK != _mali_osk_mem_reqregion(start, size, "Dedicated Mali GPU memory")) { + MALI_DEBUG_PRINT(1, ("Failed to request memory region for frame buffer (0x%08X - 0x%08X)\n", start, start + size - 1)); + return _MALI_OSK_ERR_FAULT; + } + + /* Create generic block allocator object to handle it */ + allocator = mali_mem_block_allocator_create(start, 0 /* cpu_usage_adjust */, size); + + if (NULL == allocator) { + MALI_DEBUG_PRINT(1, ("Memory bank registration failed\n")); + _mali_osk_mem_unreqregion(start, size); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + mali_mem_block_gobal_allocator = (block_allocator*)allocator; + + return _MALI_OSK_ERR_OK; +} diff --git a/drivers/gpu/arm/mali/linux/mali_memory_block_alloc.h b/drivers/gpu/arm/mali/linux/mali_memory_block_alloc.h new file mode 100644 index 00000000000000..89bd541e4dc6c2 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_memory_block_alloc.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2010, 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_BLOCK_ALLOCATOR_H__ +#define __MALI_BLOCK_ALLOCATOR_H__ + +#include "mali_session.h" +#include "mali_memory.h" + +#include "mali_memory_types.h" + +typedef struct mali_mem_allocator mali_mem_allocator; + +mali_mem_allocator *mali_block_allocator_create(u32 base_address, u32 cpu_usage_adjust, u32 size); +void mali_mem_block_allocator_destroy(mali_mem_allocator *allocator); + +mali_mem_allocation *mali_mem_block_alloc(u32 mali_addr, u32 size, struct vm_area_struct *vma, struct mali_session_data *session); +void mali_mem_block_release(mali_mem_allocation *descriptor); + +u32 mali_mem_block_allocator_stat(void); + +#endif /* __MALI_BLOCK_ALLOCATOR_H__ */ diff --git a/drivers/gpu/arm/mali/linux/mali_memory_dma_buf.c b/drivers/gpu/arm/mali/linux/mali_memory_dma_buf.c new file mode 100644 index 00000000000000..60cae152351593 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_memory_dma_buf.c @@ -0,0 +1,434 @@ +/* + * Copyright (C) 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include /* file system operations */ +#include /* user space access */ +#include +#include +#include +#include +#include +#include +#include + +#include "mali_ukk.h" +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_session.h" +#include "mali_kernel_linux.h" + +#include "mali_memory.h" +#include "mali_memory_dma_buf.h" + +#include "mali_pp_job.h" + +static void mali_dma_buf_unmap(struct mali_dma_buf_attachment *mem); + +struct mali_dma_buf_attachment { + struct dma_buf *buf; + struct dma_buf_attachment *attachment; + struct sg_table *sgt; + struct mali_session_data *session; + int map_ref; + struct mutex map_lock; + mali_bool is_mapped; + wait_queue_head_t wait_queue; +}; + +static void mali_dma_buf_release(struct mali_dma_buf_attachment *mem) +{ + MALI_DEBUG_PRINT(3, ("Mali DMA-buf: release attachment %p\n", mem)); + + MALI_DEBUG_ASSERT_POINTER(mem); + MALI_DEBUG_ASSERT_POINTER(mem->attachment); + MALI_DEBUG_ASSERT_POINTER(mem->buf); + +#if defined(CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH) + /* We mapped implicitly on attach, so we need to unmap on release */ + mali_dma_buf_unmap(mem); +#endif + + /* Wait for buffer to become unmapped */ + wait_event(mem->wait_queue, !mem->is_mapped); + MALI_DEBUG_ASSERT(!mem->is_mapped); + + dma_buf_detach(mem->buf, mem->attachment); + dma_buf_put(mem->buf); + + _mali_osk_free(mem); +} + +void mali_mem_dma_buf_release(mali_mem_allocation *descriptor) +{ + struct mali_dma_buf_attachment *mem = descriptor->dma_buf.attachment; + + mali_dma_buf_release(mem); +} + +/* + * Map DMA buf attachment \a mem into \a session at virtual address \a virt. + */ +static int mali_dma_buf_map(struct mali_dma_buf_attachment *mem, struct mali_session_data *session, u32 virt, u32 flags) +{ + struct mali_page_directory *pagedir; + struct scatterlist *sg; + int i; + + MALI_DEBUG_ASSERT_POINTER(mem); + MALI_DEBUG_ASSERT_POINTER(session); + MALI_DEBUG_ASSERT(mem->session == session); + + mutex_lock(&mem->map_lock); + + mem->map_ref++; + + MALI_DEBUG_PRINT(5, ("Mali DMA-buf: map attachment %p, new map_ref = %d\n", mem, mem->map_ref)); + + if (1 == mem->map_ref) { + /* First reference taken, so we need to map the dma buf */ + MALI_DEBUG_ASSERT(!mem->is_mapped); + + pagedir = mali_session_get_page_directory(session); + MALI_DEBUG_ASSERT_POINTER(pagedir); + + mem->sgt = dma_buf_map_attachment(mem->attachment, DMA_BIDIRECTIONAL); + if (IS_ERR_OR_NULL(mem->sgt)) { + MALI_DEBUG_PRINT_ERROR(("Failed to map dma-buf attachment\n")); + return -EFAULT; + } + + for_each_sg(mem->sgt->sgl, sg, mem->sgt->nents, i) { + u32 size = sg_dma_len(sg); + dma_addr_t phys = sg_dma_address(sg); + + /* sg must be page aligned. */ + MALI_DEBUG_ASSERT(0 == size % MALI_MMU_PAGE_SIZE); + + mali_mmu_pagedir_update(pagedir, virt, phys, size, MALI_MMU_FLAGS_DEFAULT); + + virt += size; + } + + if (flags & MALI_MEM_FLAG_MALI_GUARD_PAGE) { + u32 guard_phys; + MALI_DEBUG_PRINT(7, ("Mapping in extra guard page\n")); + + guard_phys = sg_dma_address(mem->sgt->sgl); + mali_mmu_pagedir_update(pagedir, virt, guard_phys, MALI_MMU_PAGE_SIZE, MALI_MMU_FLAGS_DEFAULT); + } + + mem->is_mapped = MALI_TRUE; + mutex_unlock(&mem->map_lock); + + /* Wake up any thread waiting for buffer to become mapped */ + wake_up_all(&mem->wait_queue); + } else { + MALI_DEBUG_ASSERT(mem->is_mapped); + mutex_unlock(&mem->map_lock); + } + + return 0; +} + +static void mali_dma_buf_unmap(struct mali_dma_buf_attachment *mem) +{ + MALI_DEBUG_ASSERT_POINTER(mem); + MALI_DEBUG_ASSERT_POINTER(mem->attachment); + MALI_DEBUG_ASSERT_POINTER(mem->buf); + + mutex_lock(&mem->map_lock); + + mem->map_ref--; + + MALI_DEBUG_PRINT(5, ("Mali DMA-buf: unmap attachment %p, new map_ref = %d\n", mem, mem->map_ref)); + + if (0 == mem->map_ref) { + dma_buf_unmap_attachment(mem->attachment, mem->sgt, DMA_BIDIRECTIONAL); + + mem->is_mapped = MALI_FALSE; + } + + mutex_unlock(&mem->map_lock); + + /* Wake up any thread waiting for buffer to become unmapped */ + wake_up_all(&mem->wait_queue); +} + +#if !defined(CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH) +int mali_dma_buf_map_job(struct mali_pp_job *job) +{ + mali_mem_allocation *descriptor; + struct mali_dma_buf_attachment *mem; + _mali_osk_errcode_t err; + int i; + int ret = 0; + + _mali_osk_mutex_wait(job->session->memory_lock); + + for (i = 0; i < job->num_memory_cookies; i++) { + int cookie = job->memory_cookies[i]; + + if (0 == cookie) { + /* 0 is not a valid cookie */ + MALI_DEBUG_ASSERT(NULL == job->dma_bufs[i]); + continue; + } + + MALI_DEBUG_ASSERT(0 < cookie); + + err = mali_descriptor_mapping_get(job->session->descriptor_mapping, + cookie, (void**)&descriptor); + + if (_MALI_OSK_ERR_OK != err) { + MALI_DEBUG_PRINT_ERROR(("Mali DMA-buf: Failed to get descriptor for cookie %d\n", cookie)); + ret = -EFAULT; + MALI_DEBUG_ASSERT(NULL == job->dma_bufs[i]); + continue; + } + + if (MALI_MEM_DMA_BUF != descriptor->type) { + /* Not a DMA-buf */ + MALI_DEBUG_ASSERT(NULL == job->dma_bufs[i]); + continue; + } + + mem = descriptor->dma_buf.attachment; + + MALI_DEBUG_ASSERT_POINTER(mem); + MALI_DEBUG_ASSERT(mem->session == job->session); + + err = mali_dma_buf_map(mem, mem->session, descriptor->mali_mapping.addr, descriptor->flags); + if (0 != err) { + MALI_DEBUG_PRINT_ERROR(("Mali DMA-buf: Failed to map dma-buf for cookie %d at mali address %x\b", + cookie, descriptor->mali_mapping.addr)); + ret = -EFAULT; + MALI_DEBUG_ASSERT(NULL == job->dma_bufs[i]); + continue; + } + + /* Add mem to list of DMA-bufs mapped for this job */ + job->dma_bufs[i] = mem; + } + + _mali_osk_mutex_signal(job->session->memory_lock); + + return ret; +} + +void mali_dma_buf_unmap_job(struct mali_pp_job *job) +{ + int i; + for (i = 0; i < job->num_dma_bufs; i++) { + if (NULL == job->dma_bufs[i]) continue; + + mali_dma_buf_unmap(job->dma_bufs[i]); + job->dma_bufs[i] = NULL; + } +} +#endif /* !CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH */ + +int mali_attach_dma_buf(struct mali_session_data *session, _mali_uk_attach_dma_buf_s __user *user_arg) +{ + struct dma_buf *buf; + struct mali_dma_buf_attachment *mem; + _mali_uk_attach_dma_buf_s args; + mali_mem_allocation *descriptor; + int md; + int fd; + + /* Get call arguments from user space. copy_from_user returns how many bytes which where NOT copied */ + if (0 != copy_from_user(&args, (void __user *)user_arg, sizeof(_mali_uk_attach_dma_buf_s))) { + return -EFAULT; + } + + if (args.mali_address & ~PAGE_MASK) { + MALI_DEBUG_PRINT_ERROR(("Requested address (0x%08x) is not page aligned\n", args.mali_address)); + return -EINVAL; + } + + if (args.mali_address >= args.mali_address + args.size) { + MALI_DEBUG_PRINT_ERROR(("Requested address and size (0x%08x + 0x%08x) is too big\n", args.mali_address, args.size)); + return -EINVAL; + } + + fd = args.mem_fd; + + buf = dma_buf_get(fd); + if (IS_ERR_OR_NULL(buf)) { + MALI_DEBUG_PRINT_ERROR(("Failed to get dma-buf from fd: %d\n", fd)); + return PTR_RET(buf); + } + + /* Currently, mapping of the full buffer are supported. */ + if (args.size != buf->size) { + MALI_DEBUG_PRINT_ERROR(("dma-buf size doesn't match mapping size.\n")); + dma_buf_put(buf); + return -EINVAL; + } + + mem = _mali_osk_calloc(1, sizeof(struct mali_dma_buf_attachment)); + if (NULL == mem) { + MALI_DEBUG_PRINT_ERROR(("Failed to allocate dma-buf tracing struct\n")); + dma_buf_put(buf); + return -ENOMEM; + } + + mem->buf = buf; + mem->session = session; + mem->map_ref = 0; + mutex_init(&mem->map_lock); + init_waitqueue_head(&mem->wait_queue); + + mem->attachment = dma_buf_attach(mem->buf, &mali_platform_device->dev); + if (NULL == mem->attachment) { + MALI_DEBUG_PRINT_ERROR(("Failed to attach to dma-buf %d\n", fd)); + dma_buf_put(mem->buf); + _mali_osk_free(mem); + return -EFAULT; + } + + /* Set up Mali memory descriptor */ + descriptor = mali_mem_descriptor_create(session, MALI_MEM_DMA_BUF); + if (NULL == descriptor) { + MALI_DEBUG_PRINT_ERROR(("Failed to allocate descriptor dma-buf %d\n", fd)); + mali_dma_buf_release(mem); + return -ENOMEM; + } + + descriptor->size = args.size; + descriptor->mali_mapping.addr = args.mali_address; + + descriptor->dma_buf.attachment = mem; + + descriptor->flags |= MALI_MEM_FLAG_DONT_CPU_MAP; + if (args.flags & _MALI_MAP_EXTERNAL_MAP_GUARD_PAGE) { + descriptor->flags = MALI_MEM_FLAG_MALI_GUARD_PAGE; + } + + _mali_osk_mutex_wait(session->memory_lock); + + /* Map dma-buf into this session's page tables */ + if (_MALI_OSK_ERR_OK != mali_mem_mali_map_prepare(descriptor)) { + _mali_osk_mutex_signal(session->memory_lock); + MALI_DEBUG_PRINT_ERROR(("Failed to map dma-buf on Mali\n")); + mali_mem_descriptor_destroy(descriptor); + mali_dma_buf_release(mem); + return -ENOMEM; + } + +#if defined(CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH) + /* Map memory into session's Mali virtual address space. */ + + if (0 != mali_dma_buf_map(mem, session, descriptor->mali_mapping.addr, descriptor->flags)) { + mali_mem_mali_map_free(descriptor); + _mali_osk_mutex_signal(session->memory_lock); + + MALI_DEBUG_PRINT_ERROR(("Failed to map dma-buf %d into Mali address space\n", fd)); + mali_mem_descriptor_destroy(descriptor); + mali_dma_buf_release(mem); + return -ENOMEM; + } + +#endif + + _mali_osk_mutex_signal(session->memory_lock); + + /* Get descriptor mapping for memory. */ + if (_MALI_OSK_ERR_OK != mali_descriptor_mapping_allocate_mapping(session->descriptor_mapping, descriptor, &md)) { + _mali_osk_mutex_wait(session->memory_lock); + mali_mem_mali_map_free(descriptor); + _mali_osk_mutex_signal(session->memory_lock); + + MALI_DEBUG_PRINT_ERROR(("Failed to create descriptor mapping for dma-buf %d\n", fd)); + mali_mem_descriptor_destroy(descriptor); + mali_dma_buf_release(mem); + return -EFAULT; + } + + /* Return stuff to user space */ + if (0 != put_user(md, &user_arg->cookie)) { + _mali_osk_mutex_wait(session->memory_lock); + mali_mem_mali_map_free(descriptor); + _mali_osk_mutex_signal(session->memory_lock); + + MALI_DEBUG_PRINT_ERROR(("Failed to return descriptor to user space for dma-buf %d\n", fd)); + mali_descriptor_mapping_free(session->descriptor_mapping, md); + mali_dma_buf_release(mem); + return -EFAULT; + } + + return 0; +} + +int mali_release_dma_buf(struct mali_session_data *session, _mali_uk_release_dma_buf_s __user *user_arg) +{ + int ret = 0; + _mali_uk_release_dma_buf_s args; + mali_mem_allocation *descriptor; + + /* get call arguments from user space. copy_from_user returns how many bytes which where NOT copied */ + if ( 0 != copy_from_user(&args, (void __user *)user_arg, sizeof(_mali_uk_release_dma_buf_s)) ) { + return -EFAULT; + } + + MALI_DEBUG_PRINT(3, ("Mali DMA-buf: release descriptor cookie %d\n", args.cookie)); + + _mali_osk_mutex_wait(session->memory_lock); + + descriptor = mali_descriptor_mapping_free(session->descriptor_mapping, args.cookie); + + if (NULL != descriptor) { + MALI_DEBUG_PRINT(3, ("Mali DMA-buf: Releasing dma-buf at mali address %x\n", descriptor->mali_mapping.addr)); + + mali_mem_mali_map_free(descriptor); + + mali_dma_buf_release(descriptor->dma_buf.attachment); + + mali_mem_descriptor_destroy(descriptor); + } else { + MALI_DEBUG_PRINT_ERROR(("Invalid memory descriptor %d used to release dma-buf\n", args.cookie)); + ret = -EINVAL; + } + + _mali_osk_mutex_signal(session->memory_lock); + + /* Return the error that _mali_ukk_map_external_ump_mem produced */ + return ret; +} + +int mali_dma_buf_get_size(struct mali_session_data *session, _mali_uk_dma_buf_get_size_s __user *user_arg) +{ + _mali_uk_dma_buf_get_size_s args; + int fd; + struct dma_buf *buf; + + /* get call arguments from user space. copy_from_user returns how many bytes which where NOT copied */ + if ( 0 != copy_from_user(&args, (void __user *)user_arg, sizeof(_mali_uk_dma_buf_get_size_s)) ) { + return -EFAULT; + } + + /* Do DMA-BUF stuff */ + fd = args.mem_fd; + + buf = dma_buf_get(fd); + if (IS_ERR_OR_NULL(buf)) { + MALI_DEBUG_PRINT_ERROR(("Failed to get dma-buf from fd: %d\n", fd)); + return PTR_RET(buf); + } + + if (0 != put_user(buf->size, &user_arg->size)) { + dma_buf_put(buf); + return -EFAULT; + } + + dma_buf_put(buf); + + return 0; +} diff --git a/drivers/gpu/arm/mali/linux/mali_memory_dma_buf.h b/drivers/gpu/arm/mali/linux/mali_memory_dma_buf.h new file mode 100644 index 00000000000000..7ee4dc54350a2d --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_memory_dma_buf.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2011-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_MEMORY_DMA_BUF_H__ +#define __MALI_MEMORY_DMA_BUF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "mali_osk.h" +#include "mali_memory.h" + +struct mali_pp_job; + +struct mali_dma_buf_attachment; + +int mali_attach_dma_buf(struct mali_session_data *session, _mali_uk_attach_dma_buf_s __user *arg); +int mali_release_dma_buf(struct mali_session_data *session, _mali_uk_release_dma_buf_s __user *arg); +int mali_dma_buf_get_size(struct mali_session_data *session, _mali_uk_dma_buf_get_size_s __user *arg); + +void mali_mem_dma_buf_release(mali_mem_allocation *descriptor); + +#if !defined(CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH) +int mali_dma_buf_map_job(struct mali_pp_job *job); +void mali_dma_buf_unmap_job(struct mali_pp_job *job); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_MEMORY_DMA_BUF_H__ */ diff --git a/drivers/gpu/arm/mali/linux/mali_memory_external.c b/drivers/gpu/arm/mali/linux/mali_memory_external.c new file mode 100644 index 00000000000000..6d8e7922f1af08 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_memory_external.c @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_osk.h" +#include "mali_memory.h" +#include "mali_kernel_descriptor_mapping.h" +#include "mali_mem_validation.h" +#include "mali_uk_types.h" + +void mali_mem_external_release(mali_mem_allocation *descriptor) +{ + MALI_DEBUG_ASSERT(MALI_MEM_EXTERNAL == descriptor->type); + + mali_mem_mali_map_free(descriptor); +} + +_mali_osk_errcode_t _mali_ukk_map_external_mem(_mali_uk_map_external_mem_s *args) +{ + struct mali_session_data *session; + mali_mem_allocation * descriptor; + int md; + _mali_osk_errcode_t err; + + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + + session = (struct mali_session_data *)args->ctx; + MALI_CHECK_NON_NULL(session, _MALI_OSK_ERR_INVALID_ARGS); + + /* check arguments */ + /* NULL might be a valid Mali address */ + if (! args->size) MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + + /* size must be a multiple of the system page size */ + if (args->size % _MALI_OSK_MALI_PAGE_SIZE) MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + + MALI_DEBUG_PRINT(3, + ("Requested to map physical memory 0x%x-0x%x into virtual memory 0x%x\n", + (void*)args->phys_addr, + (void*)(args->phys_addr + args->size -1), + (void*)args->mali_address) + ); + + /* Validate the mali physical range */ + if (_MALI_OSK_ERR_OK != mali_mem_validation_check(args->phys_addr, args->size)) { + return _MALI_OSK_ERR_FAULT; + } + + descriptor = mali_mem_descriptor_create(session, MALI_MEM_EXTERNAL); + if (NULL == descriptor) MALI_ERROR(_MALI_OSK_ERR_NOMEM); + + descriptor->mali_mapping.addr = args->mali_address; + descriptor->size = args->size; + + if (args->flags & _MALI_MAP_EXTERNAL_MAP_GUARD_PAGE) { + descriptor->flags = MALI_MEM_FLAG_MALI_GUARD_PAGE; + } + + _mali_osk_mutex_wait(session->memory_lock); + { + u32 virt = descriptor->mali_mapping.addr; + u32 phys = args->phys_addr; + u32 size = args->size; + + err = mali_mem_mali_map_prepare(descriptor); + if (_MALI_OSK_ERR_OK != err) { + _mali_osk_mutex_signal(session->memory_lock); + mali_mem_descriptor_destroy(descriptor); + return _MALI_OSK_ERR_NOMEM; + } + + mali_mmu_pagedir_update(session->page_directory, virt, phys, size, MALI_MMU_FLAGS_DEFAULT); + + if (descriptor->flags & MALI_MEM_FLAG_MALI_GUARD_PAGE) { + mali_mmu_pagedir_update(session->page_directory, virt + size, phys, _MALI_OSK_MALI_PAGE_SIZE, MALI_MMU_FLAGS_DEFAULT); + } + } + _mali_osk_mutex_signal(session->memory_lock); + + if (_MALI_OSK_ERR_OK != mali_descriptor_mapping_allocate_mapping(session->descriptor_mapping, descriptor, &md)) { + _mali_osk_mutex_wait(session->memory_lock); + mali_mem_external_release(descriptor); + _mali_osk_mutex_signal(session->memory_lock); + mali_mem_descriptor_destroy(descriptor); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + args->cookie = md; + + MALI_SUCCESS; +} + +_mali_osk_errcode_t _mali_ukk_unmap_external_mem( _mali_uk_unmap_external_mem_s *args ) +{ + mali_mem_allocation * descriptor; + void* old_value; + struct mali_session_data *session; + + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + + session = (struct mali_session_data *)args->ctx; + MALI_CHECK_NON_NULL(session, _MALI_OSK_ERR_INVALID_ARGS); + + if (_MALI_OSK_ERR_OK != mali_descriptor_mapping_get(session->descriptor_mapping, args->cookie, (void**)&descriptor)) { + MALI_DEBUG_PRINT(1, ("Invalid memory descriptor %d used to unmap external memory\n", args->cookie)); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + old_value = mali_descriptor_mapping_free(session->descriptor_mapping, args->cookie); + + if (NULL != old_value) { + _mali_osk_mutex_wait(session->memory_lock); + mali_mem_external_release(descriptor); + _mali_osk_mutex_signal(session->memory_lock); + mali_mem_descriptor_destroy(descriptor); + } + + MALI_SUCCESS; +} diff --git a/drivers/gpu/arm/mali/linux/mali_memory_os_alloc.c b/drivers/gpu/arm/mali/linux/mali_memory_os_alloc.c new file mode 100644 index 00000000000000..a9b20354f46689 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_memory_os_alloc.c @@ -0,0 +1,556 @@ +/* + * Copyright (C) 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mali_osk.h" +#include "mali_memory.h" +#include "mali_memory_os_alloc.h" +#include "mali_kernel_linux.h" + +/* Minimum size of allocator page pool */ +#define MALI_OS_MEMORY_KERNEL_BUFFER_SIZE_IN_PAGES (MALI_OS_MEMORY_KERNEL_BUFFER_SIZE_IN_MB * 256) +#define MALI_OS_MEMORY_POOL_TRIM_JIFFIES (10 * CONFIG_HZ) /* Default to 10s */ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) +static int mali_mem_os_shrink(int nr_to_scan, gfp_t gfp_mask); +#else +static int mali_mem_os_shrink(struct shrinker *shrinker, int nr_to_scan, gfp_t gfp_mask); +#endif +#else +static int mali_mem_os_shrink(struct shrinker *shrinker, struct shrink_control *sc); +#endif +static void mali_mem_os_trim_pool(struct work_struct *work); + +static struct mali_mem_os_allocator { + spinlock_t pool_lock; + struct list_head pool_pages; + size_t pool_count; + + atomic_t allocated_pages; + size_t allocation_limit; + + struct shrinker shrinker; + struct delayed_work timed_shrinker; + struct workqueue_struct *wq; +} mali_mem_os_allocator = { + .pool_lock = __SPIN_LOCK_UNLOCKED(pool_lock), + .pool_pages = LIST_HEAD_INIT(mali_mem_os_allocator.pool_pages), + .pool_count = 0, + + .allocated_pages = ATOMIC_INIT(0), + .allocation_limit = 0, + + .shrinker.shrink = mali_mem_os_shrink, + .shrinker.seeks = DEFAULT_SEEKS, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) + .timed_shrinker = __DELAYED_WORK_INITIALIZER(mali_mem_os_allocator.timed_shrinker, mali_mem_os_trim_pool, TIMER_DEFERRABLE), +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38) + .timed_shrinker = __DEFERRED_WORK_INITIALIZER(mali_mem_os_allocator.timed_shrinker, mali_mem_os_trim_pool), +#else + .timed_shrinker = __DELAYED_WORK_INITIALIZER(mali_mem_os_allocator.timed_shrinker, mali_mem_os_trim_pool), +#endif +}; + +static void mali_mem_os_free(mali_mem_allocation *descriptor) +{ + LIST_HEAD(pages); + + MALI_DEBUG_ASSERT(MALI_MEM_OS == descriptor->type); + + atomic_sub(descriptor->os_mem.count, &mali_mem_os_allocator.allocated_pages); + + /* Put pages on pool. */ + list_cut_position(&pages, &descriptor->os_mem.pages, descriptor->os_mem.pages.prev); + + spin_lock(&mali_mem_os_allocator.pool_lock); + + list_splice(&pages, &mali_mem_os_allocator.pool_pages); + mali_mem_os_allocator.pool_count += descriptor->os_mem.count; + + spin_unlock(&mali_mem_os_allocator.pool_lock); + + if (MALI_OS_MEMORY_KERNEL_BUFFER_SIZE_IN_PAGES < mali_mem_os_allocator.pool_count) { + MALI_DEBUG_PRINT(5, ("OS Mem: Starting pool trim timer %u\n", mali_mem_os_allocator.pool_count)); + queue_delayed_work(mali_mem_os_allocator.wq, &mali_mem_os_allocator.timed_shrinker, MALI_OS_MEMORY_POOL_TRIM_JIFFIES); + } +} + +static int mali_mem_os_alloc_pages(mali_mem_allocation *descriptor, u32 size) +{ + struct page *new_page, *tmp; + LIST_HEAD(pages); + size_t page_count = PAGE_ALIGN(size) / _MALI_OSK_MALI_PAGE_SIZE; + size_t remaining = page_count; + u32 i; + + MALI_DEBUG_ASSERT_POINTER(descriptor); + MALI_DEBUG_ASSERT(MALI_MEM_OS == descriptor->type); + + INIT_LIST_HEAD(&descriptor->os_mem.pages); + descriptor->os_mem.count = page_count; + + /* Grab pages from pool. */ + { + size_t pool_pages; + spin_lock(&mali_mem_os_allocator.pool_lock); + pool_pages = min(remaining, mali_mem_os_allocator.pool_count); + for (i = pool_pages; i > 0; i--) { + BUG_ON(list_empty(&mali_mem_os_allocator.pool_pages)); + list_move(mali_mem_os_allocator.pool_pages.next, &pages); + } + mali_mem_os_allocator.pool_count -= pool_pages; + remaining -= pool_pages; + spin_unlock(&mali_mem_os_allocator.pool_lock); + } + + /* Process pages from pool. */ + i = 0; + list_for_each_entry_safe(new_page, tmp, &pages, lru) { + BUG_ON(NULL == new_page); + + list_move_tail(&new_page->lru, &descriptor->os_mem.pages); + } + + /* Allocate new pages, if needed. */ + for (i = 0; i < remaining; i++) { + dma_addr_t dma_addr; + + new_page = alloc_page(GFP_HIGHUSER | __GFP_ZERO | __GFP_REPEAT | __GFP_NOWARN | __GFP_COLD); + + if (unlikely(NULL == new_page)) { + /* Calculate the number of pages actually allocated, and free them. */ + descriptor->os_mem.count = (page_count - remaining) + i; + atomic_add(descriptor->os_mem.count, &mali_mem_os_allocator.allocated_pages); + mali_mem_os_free(descriptor); + return -ENOMEM; + } + + /* Ensure page is flushed from CPU caches. */ + dma_addr = dma_map_page(&mali_platform_device->dev, new_page, + 0, _MALI_OSK_MALI_PAGE_SIZE, DMA_TO_DEVICE); + + /* Store page phys addr */ + SetPagePrivate(new_page); + set_page_private(new_page, dma_addr); + + list_add_tail(&new_page->lru, &descriptor->os_mem.pages); + } + + atomic_add(page_count, &mali_mem_os_allocator.allocated_pages); + + if (MALI_OS_MEMORY_KERNEL_BUFFER_SIZE_IN_PAGES > mali_mem_os_allocator.pool_count) { + MALI_DEBUG_PRINT(4, ("OS Mem: Stopping pool trim timer, only %u pages on pool\n", mali_mem_os_allocator.pool_count)); + cancel_delayed_work(&mali_mem_os_allocator.timed_shrinker); + } + + return 0; +} + +static int mali_mem_os_mali_map(mali_mem_allocation *descriptor, struct mali_session_data *session) +{ + struct mali_page_directory *pagedir = session->page_directory; + struct page *page; + _mali_osk_errcode_t err; + u32 virt = descriptor->mali_mapping.addr; + u32 prop = descriptor->mali_mapping.properties; + + MALI_DEBUG_ASSERT(MALI_MEM_OS == descriptor->type); + + err = mali_mem_mali_map_prepare(descriptor); + if (_MALI_OSK_ERR_OK != err) { + return -ENOMEM; + } + + list_for_each_entry(page, &descriptor->os_mem.pages, lru) { + u32 phys = page_private(page); + mali_mmu_pagedir_update(pagedir, virt, phys, MALI_MMU_PAGE_SIZE, prop); + virt += MALI_MMU_PAGE_SIZE; + } + + return 0; +} + +static void mali_mem_os_mali_unmap(struct mali_session_data *session, mali_mem_allocation *descriptor) +{ + mali_mem_mali_map_free(descriptor); +} + +static int mali_mem_os_cpu_map(mali_mem_allocation *descriptor, struct vm_area_struct *vma) +{ + struct page *page; + int ret; + unsigned long addr = vma->vm_start; + + list_for_each_entry(page, &descriptor->os_mem.pages, lru) { + /* We should use vm_insert_page, but it does a dcache + * flush which makes it way slower than remap_pfn_range or vm_insert_pfn. + ret = vm_insert_page(vma, addr, page); + */ + ret = vm_insert_pfn(vma, addr, page_to_pfn(page)); + + if (unlikely(0 != ret)) { + return -EFAULT; + } + addr += _MALI_OSK_MALI_PAGE_SIZE; + } + + return 0; +} + +mali_mem_allocation *mali_mem_os_alloc(u32 mali_addr, u32 size, struct vm_area_struct *vma, struct mali_session_data *session) +{ + mali_mem_allocation *descriptor; + int err; + + if (atomic_read(&mali_mem_os_allocator.allocated_pages) * _MALI_OSK_MALI_PAGE_SIZE + size > mali_mem_os_allocator.allocation_limit) { + MALI_DEBUG_PRINT(2, ("Mali Mem: Unable to allocate %u bytes. Currently allocated: %lu, max limit %lu\n", + size, + atomic_read(&mali_mem_os_allocator.allocated_pages) * _MALI_OSK_MALI_PAGE_SIZE, + mali_mem_os_allocator.allocation_limit)); + return NULL; + } + + descriptor = mali_mem_descriptor_create(session, MALI_MEM_OS); + if (NULL == descriptor) return NULL; + + descriptor->mali_mapping.addr = mali_addr; + descriptor->size = size; + descriptor->cpu_mapping.addr = (void __user*)vma->vm_start; + descriptor->cpu_mapping.ref = 1; + + if (VM_SHARED == (VM_SHARED & vma->vm_flags)) { + descriptor->mali_mapping.properties = MALI_MMU_FLAGS_DEFAULT; + } else { + /* Cached Mali memory mapping */ + descriptor->mali_mapping.properties = MALI_MMU_FLAGS_FORCE_GP_READ_ALLOCATE; + vma->vm_flags |= VM_SHARED; + } + + err = mali_mem_os_alloc_pages(descriptor, size); /* Allocate pages */ + if (0 != err) goto alloc_failed; + + /* Take session memory lock */ + _mali_osk_mutex_wait(session->memory_lock); + + err = mali_mem_os_mali_map(descriptor, session); /* Map on Mali */ + if (0 != err) goto mali_map_failed; + + _mali_osk_mutex_signal(session->memory_lock); + + err = mali_mem_os_cpu_map(descriptor, vma); /* Map on CPU */ + if (0 != err) goto cpu_map_failed; + + return descriptor; + +cpu_map_failed: + mali_mem_os_mali_unmap(session, descriptor); +mali_map_failed: + _mali_osk_mutex_signal(session->memory_lock); + mali_mem_os_free(descriptor); +alloc_failed: + mali_mem_descriptor_destroy(descriptor); + MALI_DEBUG_PRINT(2, ("OS allocator: Failed to allocate memory (%d)\n", err)); + return NULL; +} + +void mali_mem_os_release(mali_mem_allocation *descriptor) +{ + struct mali_session_data *session = descriptor->session; + + /* Unmap the memory from the mali virtual address space. */ + mali_mem_os_mali_unmap(session, descriptor); + + /* Free pages */ + mali_mem_os_free(descriptor); +} + + +#define MALI_MEM_OS_PAGE_TABLE_PAGE_POOL_SIZE 128 +static struct { + struct { + u32 phys; + mali_io_address mapping; + } page[MALI_MEM_OS_PAGE_TABLE_PAGE_POOL_SIZE]; + u32 count; + spinlock_t lock; +} mali_mem_page_table_page_pool = { + .count = 0, + .lock = __SPIN_LOCK_UNLOCKED(pool_lock), +}; + +_mali_osk_errcode_t mali_mem_os_get_table_page(u32 *phys, mali_io_address *mapping) +{ + _mali_osk_errcode_t ret = _MALI_OSK_ERR_NOMEM; + + spin_lock(&mali_mem_page_table_page_pool.lock); + if (0 < mali_mem_page_table_page_pool.count) { + u32 i = --mali_mem_page_table_page_pool.count; + *phys = mali_mem_page_table_page_pool.page[i].phys; + *mapping = mali_mem_page_table_page_pool.page[i].mapping; + + ret = _MALI_OSK_ERR_OK; + } + spin_unlock(&mali_mem_page_table_page_pool.lock); + + if (_MALI_OSK_ERR_OK != ret) { + *mapping = dma_alloc_writecombine(&mali_platform_device->dev, _MALI_OSK_MALI_PAGE_SIZE, phys, GFP_KERNEL); + if (NULL != *mapping) { + ret = _MALI_OSK_ERR_OK; + } + } + + return ret; +} + +void mali_mem_os_release_table_page(u32 phys, void *virt) +{ + spin_lock(&mali_mem_page_table_page_pool.lock); + if (MALI_MEM_OS_PAGE_TABLE_PAGE_POOL_SIZE > mali_mem_page_table_page_pool.count) { + u32 i = mali_mem_page_table_page_pool.count; + mali_mem_page_table_page_pool.page[i].phys = phys; + mali_mem_page_table_page_pool.page[i].mapping = virt; + + ++mali_mem_page_table_page_pool.count; + + spin_unlock(&mali_mem_page_table_page_pool.lock); + } else { + spin_unlock(&mali_mem_page_table_page_pool.lock); + + dma_free_writecombine(&mali_platform_device->dev, _MALI_OSK_MALI_PAGE_SIZE, virt, phys); + } +} + +static void mali_mem_os_free_page(struct page *page) +{ + BUG_ON(page_count(page) != 1); + + dma_unmap_page(&mali_platform_device->dev, page_private(page), + _MALI_OSK_MALI_PAGE_SIZE, DMA_TO_DEVICE); + + ClearPagePrivate(page); + + __free_page(page); +} + +/* The maximum number of page table pool pages to free in one go. */ +#define MALI_MEM_OS_CHUNK_TO_FREE 64UL + +/* Free a certain number of pages from the page table page pool. + * The pool lock must be held when calling the function, and the lock will be + * released before returning. + */ +static void mali_mem_os_page_table_pool_free(size_t nr_to_free) +{ + u32 phys_arr[MALI_MEM_OS_CHUNK_TO_FREE]; + void *virt_arr[MALI_MEM_OS_CHUNK_TO_FREE]; + u32 i; + + MALI_DEBUG_ASSERT(nr_to_free <= MALI_MEM_OS_CHUNK_TO_FREE); + + /* Remove nr_to_free pages from the pool and store them locally on stack. */ + for (i = 0; i < nr_to_free; i++) { + u32 pool_index = mali_mem_page_table_page_pool.count - i - 1; + + phys_arr[i] = mali_mem_page_table_page_pool.page[pool_index].phys; + virt_arr[i] = mali_mem_page_table_page_pool.page[pool_index].mapping; + } + + mali_mem_page_table_page_pool.count -= nr_to_free; + + spin_unlock(&mali_mem_page_table_page_pool.lock); + + /* After releasing the spinlock: free the pages we removed from the pool. */ + for (i = 0; i < nr_to_free; i++) { + dma_free_writecombine(&mali_platform_device->dev, _MALI_OSK_MALI_PAGE_SIZE, virt_arr[i], phys_arr[i]); + } +} + +static void mali_mem_os_trim_page_table_page_pool(void) +{ + size_t nr_to_free = 0; + size_t nr_to_keep; + + /* Keep 2 page table pages for each 1024 pages in the page cache. */ + nr_to_keep = mali_mem_os_allocator.pool_count / 512; + /* And a minimum of eight pages, to accomodate new sessions. */ + nr_to_keep += 8; + + if (0 == spin_trylock(&mali_mem_page_table_page_pool.lock)) return; + + if (nr_to_keep < mali_mem_page_table_page_pool.count) { + nr_to_free = mali_mem_page_table_page_pool.count - nr_to_keep; + nr_to_free = min((size_t)MALI_MEM_OS_CHUNK_TO_FREE, nr_to_free); + } + + /* Pool lock will be released by the callee. */ + mali_mem_os_page_table_pool_free(nr_to_free); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) +static int mali_mem_os_shrink(int nr_to_scan, gfp_t gfp_mask) +#else +static int mali_mem_os_shrink(struct shrinker *shrinker, int nr_to_scan, gfp_t gfp_mask) +#endif +#else +static int mali_mem_os_shrink(struct shrinker *shrinker, struct shrink_control *sc) +#endif +{ + struct page *page, *tmp; + unsigned long flags; + struct list_head *le, pages; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) + int nr = nr_to_scan; +#else + int nr = sc->nr_to_scan; +#endif + + if (0 == nr) { + return mali_mem_os_allocator.pool_count + mali_mem_page_table_page_pool.count; + } + + if (0 == mali_mem_os_allocator.pool_count) { + /* No pages availble */ + return 0; + } + + if (0 == spin_trylock_irqsave(&mali_mem_os_allocator.pool_lock, flags)) { + /* Not able to lock. */ + return -1; + } + + /* Release from general page pool */ + nr = min((size_t)nr, mali_mem_os_allocator.pool_count); + mali_mem_os_allocator.pool_count -= nr; + list_for_each(le, &mali_mem_os_allocator.pool_pages) { + --nr; + if (0 == nr) break; + } + list_cut_position(&pages, &mali_mem_os_allocator.pool_pages, le); + spin_unlock_irqrestore(&mali_mem_os_allocator.pool_lock, flags); + + list_for_each_entry_safe(page, tmp, &pages, lru) { + mali_mem_os_free_page(page); + } + + /* Release some pages from page table page pool */ + mali_mem_os_trim_page_table_page_pool(); + + if (MALI_OS_MEMORY_KERNEL_BUFFER_SIZE_IN_PAGES > mali_mem_os_allocator.pool_count) { + /* Pools are empty, stop timer */ + MALI_DEBUG_PRINT(5, ("Stopping timer, only %u pages on pool\n", mali_mem_os_allocator.pool_count)); + cancel_delayed_work(&mali_mem_os_allocator.timed_shrinker); + } + + return mali_mem_os_allocator.pool_count + mali_mem_page_table_page_pool.count; +} + +static void mali_mem_os_trim_pool(struct work_struct *data) +{ + struct page *page, *tmp; + struct list_head *le; + LIST_HEAD(pages); + size_t nr_to_free; + + MALI_IGNORE(data); + + MALI_DEBUG_PRINT(3, ("OS Mem: Trimming pool %u\n", mali_mem_os_allocator.pool_count)); + + /* Release from general page pool */ + spin_lock(&mali_mem_os_allocator.pool_lock); + if (MALI_OS_MEMORY_KERNEL_BUFFER_SIZE_IN_PAGES < mali_mem_os_allocator.pool_count) { + size_t count = mali_mem_os_allocator.pool_count - MALI_OS_MEMORY_KERNEL_BUFFER_SIZE_IN_PAGES; + /* Free half the pages on the pool above the static limit. Or 64 pages, 256KB. */ + nr_to_free = max(count / 2, (size_t)64); + + mali_mem_os_allocator.pool_count -= nr_to_free; + list_for_each(le, &mali_mem_os_allocator.pool_pages) { + --nr_to_free; + if (0 == nr_to_free) break; + } + list_cut_position(&pages, &mali_mem_os_allocator.pool_pages, le); + } + spin_unlock(&mali_mem_os_allocator.pool_lock); + + list_for_each_entry_safe(page, tmp, &pages, lru) { + mali_mem_os_free_page(page); + } + + /* Release some pages from page table page pool */ + mali_mem_os_trim_page_table_page_pool(); + + if (MALI_OS_MEMORY_KERNEL_BUFFER_SIZE_IN_PAGES < mali_mem_os_allocator.pool_count) { + MALI_DEBUG_PRINT(4, ("OS Mem: Starting pool trim timer %u\n", mali_mem_os_allocator.pool_count)); + queue_delayed_work(mali_mem_os_allocator.wq, &mali_mem_os_allocator.timed_shrinker, MALI_OS_MEMORY_POOL_TRIM_JIFFIES); + } +} + +_mali_osk_errcode_t mali_mem_os_init(void) +{ + mali_mem_os_allocator.wq = alloc_workqueue("mali-mem", WQ_UNBOUND, 1); + if (NULL == mali_mem_os_allocator.wq) { + return _MALI_OSK_ERR_NOMEM; + } + + register_shrinker(&mali_mem_os_allocator.shrinker); + + return _MALI_OSK_ERR_OK; +} + +void mali_mem_os_term(void) +{ + struct page *page, *tmp; + + unregister_shrinker(&mali_mem_os_allocator.shrinker); + cancel_delayed_work_sync(&mali_mem_os_allocator.timed_shrinker); + destroy_workqueue(mali_mem_os_allocator.wq); + + spin_lock(&mali_mem_os_allocator.pool_lock); + list_for_each_entry_safe(page, tmp, &mali_mem_os_allocator.pool_pages, lru) { + mali_mem_os_free_page(page); + + --mali_mem_os_allocator.pool_count; + } + BUG_ON(mali_mem_os_allocator.pool_count); + spin_unlock(&mali_mem_os_allocator.pool_lock); + + /* Release from page table page pool */ + do { + u32 nr_to_free; + + spin_lock(&mali_mem_page_table_page_pool.lock); + + nr_to_free = min((size_t)MALI_MEM_OS_CHUNK_TO_FREE, mali_mem_page_table_page_pool.count); + + /* Pool lock will be released by the callee. */ + mali_mem_os_page_table_pool_free(nr_to_free); + } while (0 != mali_mem_page_table_page_pool.count); +} + +_mali_osk_errcode_t mali_memory_core_resource_os_memory(u32 size) +{ + mali_mem_os_allocator.allocation_limit = size; + + MALI_SUCCESS; +} + +u32 mali_mem_os_stat(void) +{ + return atomic_read(&mali_mem_os_allocator.allocated_pages) * _MALI_OSK_MALI_PAGE_SIZE; +} diff --git a/drivers/gpu/arm/mali/linux/mali_memory_os_alloc.h b/drivers/gpu/arm/mali/linux/mali_memory_os_alloc.h new file mode 100644 index 00000000000000..12d3f4f5a006e8 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_memory_os_alloc.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_MEMORY_OS_ALLOC_H__ +#define __MALI_MEMORY_OS_ALLOC_H__ + +#include "mali_osk.h" +#include "mali_session.h" + +#include "mali_memory_types.h" + +/* OS memory allocator */ +/** @brief Allocate memory from OS + * + * This function will create a descriptor, allocate pages and map these on the CPU and Mali. + * + * @param mali_addr Mali virtual address to use for Mali mapping + * @param size Size to allocate + * @param vma Pointer to vma for CPU mapping + * @param session Pointer to session doing the allocation + */ +mali_mem_allocation *mali_mem_os_alloc(u32 mali_addr, u32 size, struct vm_area_struct *vma, struct mali_session_data *session); + +/** @brief Release Mali OS memory + * + * The session memory_lock must be held when calling this function. + * + * @param descriptor Pointer to the descriptor to release + */ +void mali_mem_os_release(mali_mem_allocation *descriptor); + +_mali_osk_errcode_t mali_mem_os_get_table_page(u32 *phys, mali_io_address *mapping); + +void mali_mem_os_release_table_page(u32 phys, void *virt); + +_mali_osk_errcode_t mali_mem_os_init(void); +void mali_mem_os_term(void); +u32 mali_mem_os_stat(void); + +#endif /* __MALI_MEMORY_OS_ALLOC_H__ */ diff --git a/drivers/gpu/arm/mali/linux/mali_memory_types.h b/drivers/gpu/arm/mali/linux/mali_memory_types.h new file mode 100644 index 00000000000000..10672785a2339b --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_memory_types.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_MEMORY_TYPES_H__ +#define __MALI_MEMORY_TYPES_H__ + +#if defined(CONFIG_MALI400_UMP) +#include "ump_kernel_interface.h" +#endif + +typedef u32 mali_address_t; + +typedef enum mali_mem_type { + MALI_MEM_OS, + MALI_MEM_EXTERNAL, + MALI_MEM_DMA_BUF, + MALI_MEM_UMP, + MALI_MEM_BLOCK, +} mali_mem_type; + +typedef struct mali_mem_os_mem { + struct list_head pages; + u32 count; +} mali_mem_os_mem; + +typedef struct mali_mem_dma_buf { +#if defined(CONFIG_DMA_SHARED_BUFFER) + struct mali_dma_buf_attachment *attachment; +#endif +} mali_mem_dma_buf; + +typedef struct mali_mem_external { + dma_addr_t phys; + u32 size; +} mali_mem_external; + +typedef struct mali_mem_ump { +#if defined(CONFIG_MALI400_UMP) + ump_dd_handle handle; +#endif +} mali_mem_ump; + +typedef struct block_allocator_allocation { + /* The list will be released in reverse order */ + struct block_info *last_allocated; + u32 mapping_length; + struct block_allocator *info; +} block_allocator_allocation; + +typedef struct mali_mem_block_mem { + block_allocator_allocation mem; +} mali_mem_block_mem; + +typedef struct mali_mem_virt_mali_mapping { + mali_address_t addr; /* Virtual Mali address */ + u32 properties; /* MMU Permissions + cache, must match MMU HW */ +} mali_mem_virt_mali_mapping; + +typedef struct mali_mem_virt_cpu_mapping { + void __user *addr; + u32 ref; +} mali_mem_virt_cpu_mapping; + +#define MALI_MEM_ALLOCATION_VALID_MAGIC 0xdeda110c +#define MALI_MEM_ALLOCATION_FREED_MAGIC 0x10101010 + +typedef struct mali_mem_allocation { + MALI_DEBUG_CODE(u32 magic); + mali_mem_type type; /**< Type of memory */ + int id; /**< ID in the descriptor map for this allocation */ + + u32 size; /**< Size of the allocation */ + u32 flags; /**< Flags for this allocation */ + + struct mali_session_data *session; /**< Pointer to session that owns the allocation */ + + /* Union selected by type. */ + union { + mali_mem_os_mem os_mem; /**< MALI_MEM_OS */ + mali_mem_external ext_mem; /**< MALI_MEM_EXTERNAL */ + mali_mem_dma_buf dma_buf; /**< MALI_MEM_DMA_BUF */ + mali_mem_ump ump_mem; /**< MALI_MEM_UMP */ + mali_mem_block_mem block_mem; /**< MALI_MEM_BLOCK */ + }; + + mali_mem_virt_cpu_mapping cpu_mapping; /**< CPU mapping */ + mali_mem_virt_mali_mapping mali_mapping; /**< Mali mapping */ +} mali_mem_allocation; + +#define MALI_MEM_FLAG_MALI_GUARD_PAGE (1 << 0) +#define MALI_MEM_FLAG_DONT_CPU_MAP (1 << 1) + +#endif /* __MALI_MEMORY_TYPES__ */ diff --git a/drivers/gpu/arm/mali/linux/mali_memory_ump.c b/drivers/gpu/arm/mali/linux/mali_memory_ump.c new file mode 100644 index 00000000000000..1115c1d1add0bc --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_memory_ump.c @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_ukk.h" +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_session.h" +#include "mali_kernel_linux.h" + +#include "mali_memory.h" + +#include "ump_kernel_interface.h" + +static int mali_ump_map(struct mali_session_data *session, mali_mem_allocation *descriptor) +{ + ump_dd_handle ump_mem; + u32 nr_blocks; + u32 i; + ump_dd_physical_block *ump_blocks; + struct mali_page_directory *pagedir; + u32 offset = 0; + u32 prop; + _mali_osk_errcode_t err; + + MALI_DEBUG_ASSERT_POINTER(session); + MALI_DEBUG_ASSERT_POINTER(descriptor); + MALI_DEBUG_ASSERT(MALI_MEM_UMP == descriptor->type); + + ump_mem = descriptor->ump_mem.handle; + MALI_DEBUG_ASSERT(UMP_DD_HANDLE_INVALID != ump_mem); + + nr_blocks = ump_dd_phys_block_count_get(ump_mem); + if (nr_blocks == 0) { + MALI_DEBUG_PRINT(1, ("No block count\n")); + return -EINVAL; + } + + ump_blocks = _mali_osk_malloc(sizeof(*ump_blocks)*nr_blocks); + if (NULL == ump_blocks) { + return -ENOMEM; + } + + if (UMP_DD_INVALID == ump_dd_phys_blocks_get(ump_mem, ump_blocks, nr_blocks)) { + _mali_osk_free(ump_blocks); + return -EFAULT; + } + + pagedir = session->page_directory; + prop = descriptor->mali_mapping.properties; + + err = mali_mem_mali_map_prepare(descriptor); + if (_MALI_OSK_ERR_OK != err) { + MALI_DEBUG_PRINT(1, ("Mapping of UMP memory failed\n")); + + _mali_osk_free(ump_blocks); + return -ENOMEM; + } + + for(i = 0; i < nr_blocks; ++i) { + u32 virt = descriptor->mali_mapping.addr + offset; + + MALI_DEBUG_PRINT(7, ("Mapping in 0x%08x size %d\n", ump_blocks[i].addr , ump_blocks[i].size)); + + mali_mmu_pagedir_update(pagedir, virt, ump_blocks[i].addr, + ump_blocks[i].size, prop); + + offset += ump_blocks[i].size; + } + + if (descriptor->flags & _MALI_MAP_EXTERNAL_MAP_GUARD_PAGE) { + u32 virt = descriptor->mali_mapping.addr + offset; + + /* Map in an extra virtual guard page at the end of the VMA */ + MALI_DEBUG_PRINT(6, ("Mapping in extra guard page\n")); + + mali_mmu_pagedir_update(pagedir, virt, ump_blocks[0].addr, _MALI_OSK_MALI_PAGE_SIZE, prop); + + offset += _MALI_OSK_MALI_PAGE_SIZE; + } + + _mali_osk_free(ump_blocks); + + return 0; +} + +void mali_ump_unmap(struct mali_session_data *session, mali_mem_allocation *descriptor) +{ + ump_dd_handle ump_mem; + struct mali_page_directory *pagedir; + + ump_mem = descriptor->ump_mem.handle; + pagedir = session->page_directory; + + MALI_DEBUG_ASSERT(UMP_DD_HANDLE_INVALID != ump_mem); + + mali_mem_mali_map_free(descriptor); + + ump_dd_reference_release(ump_mem); + return; +} + +_mali_osk_errcode_t _mali_ukk_attach_ump_mem(_mali_uk_attach_ump_mem_s *args) +{ + ump_dd_handle ump_mem; + struct mali_session_data *session; + mali_mem_allocation *descriptor; + int md, ret; + + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + + session = (struct mali_session_data *)args->ctx; + MALI_CHECK_NON_NULL(session, _MALI_OSK_ERR_INVALID_ARGS); + + /* check arguments */ + /* NULL might be a valid Mali address */ + if (!args->size) MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + + /* size must be a multiple of the system page size */ + if (args->size % _MALI_OSK_MALI_PAGE_SIZE) MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + + MALI_DEBUG_PRINT(3, + ("Requested to map ump memory with secure id %d into virtual memory 0x%08X, size 0x%08X\n", + args->secure_id, args->mali_address, args->size)); + + ump_mem = ump_dd_handle_create_from_secure_id((int)args->secure_id); + + if (UMP_DD_HANDLE_INVALID == ump_mem) MALI_ERROR(_MALI_OSK_ERR_FAULT); + + descriptor = mali_mem_descriptor_create(session, MALI_MEM_UMP); + if (NULL == descriptor) { + ump_dd_reference_release(ump_mem); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + descriptor->ump_mem.handle = ump_mem; + descriptor->mali_mapping.addr = args->mali_address; + descriptor->size = args->size; + descriptor->mali_mapping.properties = MALI_MMU_FLAGS_DEFAULT; + descriptor->flags |= MALI_MEM_FLAG_DONT_CPU_MAP; + + if (args->flags & _MALI_MAP_EXTERNAL_MAP_GUARD_PAGE) { + descriptor->flags = MALI_MEM_FLAG_MALI_GUARD_PAGE; + } + + _mali_osk_mutex_wait(session->memory_lock); + + ret = mali_ump_map(session, descriptor); + if (0 != ret) { + _mali_osk_mutex_signal(session->memory_lock); + ump_dd_reference_release(ump_mem); + mali_mem_descriptor_destroy(descriptor); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + _mali_osk_mutex_signal(session->memory_lock); + + + if (_MALI_OSK_ERR_OK != mali_descriptor_mapping_allocate_mapping(session->descriptor_mapping, descriptor, &md)) { + ump_dd_reference_release(ump_mem); + mali_mem_descriptor_destroy(descriptor); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + args->cookie = md; + + MALI_DEBUG_PRINT(5,("Returning from UMP attach\n")); + + MALI_SUCCESS; +} + +void mali_mem_ump_release(mali_mem_allocation *descriptor) +{ + struct mali_session_data *session = descriptor->session; + + MALI_DEBUG_ASSERT(MALI_MEM_UMP == descriptor->type); + + mali_ump_unmap(session, descriptor); +} + +_mali_osk_errcode_t _mali_ukk_release_ump_mem(_mali_uk_release_ump_mem_s *args) +{ + mali_mem_allocation * descriptor; + struct mali_session_data *session; + + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + + session = (struct mali_session_data *)args->ctx; + MALI_CHECK_NON_NULL(session, _MALI_OSK_ERR_INVALID_ARGS); + + if (_MALI_OSK_ERR_OK != mali_descriptor_mapping_get(session->descriptor_mapping, args->cookie, (void**)&descriptor)) { + MALI_DEBUG_PRINT(1, ("Invalid memory descriptor %d used to release ump memory\n", args->cookie)); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + descriptor = mali_descriptor_mapping_free(session->descriptor_mapping, args->cookie); + + if (NULL != descriptor) { + _mali_osk_mutex_wait(session->memory_lock); + mali_mem_ump_release(descriptor); + _mali_osk_mutex_signal(session->memory_lock); + + mali_mem_descriptor_destroy(descriptor); + } + + MALI_SUCCESS; +} diff --git a/drivers/gpu/arm/mali/linux/mali_osk_atomics.c b/drivers/gpu/arm/mali/linux/mali_osk_atomics.c new file mode 100644 index 00000000000000..7b34ee2f35f912 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_atomics.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2010, 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_atomics.c + * Implementation of the OS abstraction layer for the kernel device driver + */ + +#include "mali_osk.h" +#include +#include "mali_kernel_common.h" + +void _mali_osk_atomic_dec( _mali_osk_atomic_t *atom ) +{ + atomic_dec((atomic_t *)&atom->u.val); +} + +u32 _mali_osk_atomic_dec_return( _mali_osk_atomic_t *atom ) +{ + return atomic_dec_return((atomic_t *)&atom->u.val); +} + +void _mali_osk_atomic_inc( _mali_osk_atomic_t *atom ) +{ + atomic_inc((atomic_t *)&atom->u.val); +} + +u32 _mali_osk_atomic_inc_return( _mali_osk_atomic_t *atom ) +{ + return atomic_inc_return((atomic_t *)&atom->u.val); +} + +_mali_osk_errcode_t _mali_osk_atomic_init( _mali_osk_atomic_t *atom, u32 val ) +{ + MALI_CHECK_NON_NULL(atom, _MALI_OSK_ERR_INVALID_ARGS); + atomic_set((atomic_t *)&atom->u.val, val); + return _MALI_OSK_ERR_OK; +} + +u32 _mali_osk_atomic_read( _mali_osk_atomic_t *atom ) +{ + return atomic_read((atomic_t *)&atom->u.val); +} + +void _mali_osk_atomic_term( _mali_osk_atomic_t *atom ) +{ + MALI_IGNORE(atom); +} + +u32 _mali_osk_atomic_xchg( _mali_osk_atomic_t *atom, u32 val ) +{ + return atomic_xchg((atomic_t*)&atom->u.val, val); +} diff --git a/drivers/gpu/arm/mali/linux/mali_osk_irq.c b/drivers/gpu/arm/mali/linux/mali_osk_irq.c new file mode 100644 index 00000000000000..357659984bd447 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_irq.c @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_irq.c + * Implementation of the OS abstraction layer for the kernel device driver + */ + +#include /* For memory allocation */ +#include +#include +#include + +#include "mali_osk.h" +#include "mali_kernel_common.h" + +typedef struct _mali_osk_irq_t_struct { + u32 irqnum; + void *data; + _mali_osk_irq_uhandler_t uhandler; +} mali_osk_irq_object_t; + +typedef irqreturn_t (*irq_handler_func_t)(int, void *, struct pt_regs *); +static irqreturn_t irq_handler_upper_half (int port_name, void* dev_id ); /* , struct pt_regs *regs*/ + +#if defined(DEBUG) +#if 0 + +struct test_interrupt_data { + _mali_osk_irq_ack_t ack_func; + void *probe_data; + mali_bool interrupt_received; + wait_queue_head_t wq; +}; + +static irqreturn_t test_interrupt_upper_half(int port_name, void *dev_id) +{ + irqreturn_t ret = IRQ_NONE; + struct test_interrupt_data *data = (struct test_interrupt_data *)dev_id; + + if (_MALI_OSK_ERR_OK == data->ack_func(data->probe_data)) { + data->interrupt_received = MALI_TRUE; + wake_up(&data->wq); + ret = IRQ_HANDLED; + } + + return ret; +} + +static _mali_osk_errcode_t test_interrupt(u32 irqnum, + _mali_osk_irq_trigger_t trigger_func, + _mali_osk_irq_ack_t ack_func, + void *probe_data, + const char *description) +{ + unsigned long irq_flags = 0; + struct test_interrupt_data data = { + .ack_func = ack_func, + .probe_data = probe_data, + .interrupt_received = MALI_FALSE, + }; + +#if defined(CONFIG_MALI_SHARED_INTERRUPTS) + irq_flags |= IRQF_SHARED; +#endif /* defined(CONFIG_MALI_SHARED_INTERRUPTS) */ + + if (0 != request_irq(irqnum, test_interrupt_upper_half, irq_flags, description, &data)) { + MALI_DEBUG_PRINT(2, ("Unable to install test IRQ handler for core '%s'\n", description)); + return _MALI_OSK_ERR_FAULT; + } + + init_waitqueue_head(&data.wq); + + trigger_func(probe_data); + wait_event_timeout(data.wq, data.interrupt_received, 100); + + free_irq(irqnum, &data); + + if (data.interrupt_received) { + MALI_DEBUG_PRINT(3, ("%s: Interrupt test OK\n", description)); + return _MALI_OSK_ERR_OK; + } else { + MALI_PRINT_ERROR(("%s: Failed interrupt test on %u\n", description, irqnum)); + return _MALI_OSK_ERR_FAULT; + } +} +#endif + +#endif /* defined(DEBUG) */ + +_mali_osk_irq_t *_mali_osk_irq_init( u32 irqnum, _mali_osk_irq_uhandler_t uhandler, void *int_data, _mali_osk_irq_trigger_t trigger_func, _mali_osk_irq_ack_t ack_func, void *probe_data, const char *description ) +{ + mali_osk_irq_object_t *irq_object; + unsigned long irq_flags = 0; + +#if defined(CONFIG_MALI_SHARED_INTERRUPTS) + irq_flags |= IRQF_SHARED; +#endif /* defined(CONFIG_MALI_SHARED_INTERRUPTS) */ + + irq_object = kmalloc(sizeof(mali_osk_irq_object_t), GFP_KERNEL); + if (NULL == irq_object) { + return NULL; + } + + if (-1 == irqnum) { + /* Probe for IRQ */ + if ( (NULL != trigger_func) && (NULL != ack_func) ) { + unsigned long probe_count = 3; + _mali_osk_errcode_t err; + int irq; + + MALI_DEBUG_PRINT(2, ("Probing for irq\n")); + + do { + unsigned long mask; + + mask = probe_irq_on(); + trigger_func(probe_data); + + _mali_osk_time_ubusydelay(5); + + irq = probe_irq_off(mask); + err = ack_func(probe_data); + } while (irq < 0 && (err == _MALI_OSK_ERR_OK) && probe_count--); + + if (irq < 0 || (_MALI_OSK_ERR_OK != err)) irqnum = -1; + else irqnum = irq; + } else irqnum = -1; /* no probe functions, fault */ + + if (-1 != irqnum) { + /* found an irq */ + MALI_DEBUG_PRINT(2, ("Found irq %d\n", irqnum)); + } else { + MALI_DEBUG_PRINT(2, ("Probe for irq failed\n")); + } + } + + irq_object->irqnum = irqnum; + irq_object->uhandler = uhandler; + irq_object->data = int_data; + + if (-1 == irqnum) { + MALI_DEBUG_PRINT(2, ("No IRQ for core '%s' found during probe\n", description)); + kfree(irq_object); + return NULL; + } + +#if defined(DEBUG) +#if 0 + /* Verify that the configured interrupt settings are working */ + if (_MALI_OSK_ERR_OK != test_interrupt(irqnum, trigger_func, ack_func, probe_data, description)) { + MALI_DEBUG_PRINT(2, ("Test of IRQ handler for core '%s' failed\n", description)); + kfree(irq_object); + return NULL; + } +#endif +#endif + + if (0 != request_irq(irqnum, irq_handler_upper_half, irq_flags, description, irq_object)) { + MALI_DEBUG_PRINT(2, ("Unable to install IRQ handler for core '%s'\n", description)); + kfree(irq_object); + return NULL; + } + + return irq_object; +} + +void _mali_osk_irq_term( _mali_osk_irq_t *irq ) +{ + mali_osk_irq_object_t *irq_object = (mali_osk_irq_object_t *)irq; + free_irq(irq_object->irqnum, irq_object); + kfree(irq_object); +} + + +/** This function is called directly in interrupt context from the OS just after + * the CPU get the hw-irq from mali, or other devices on the same IRQ-channel. + * It is registered one of these function for each mali core. When an interrupt + * arrives this function will be called equal times as registered mali cores. + * That means that we only check one mali core in one function call, and the + * core we check for each turn is given by the \a dev_id variable. + * If we detect an pending interrupt on the given core, we mask the interrupt + * out by settging the core's IRQ_MASK register to zero. + * Then we schedule the mali_core_irq_handler_bottom_half to run as high priority + * work queue job. + */ +static irqreturn_t irq_handler_upper_half (int port_name, void* dev_id ) /* , struct pt_regs *regs*/ +{ + irqreturn_t ret = IRQ_NONE; + mali_osk_irq_object_t *irq_object = (mali_osk_irq_object_t *)dev_id; + + if (_MALI_OSK_ERR_OK == irq_object->uhandler(irq_object->data)) { + ret = IRQ_HANDLED; + } + + return ret; +} diff --git a/drivers/gpu/arm/mali/linux/mali_osk_locks.c b/drivers/gpu/arm/mali/linux/mali_osk_locks.c new file mode 100644 index 00000000000000..5ad3f98e0f9982 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_locks.c @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_locks.c + * Implemenation of the OS abstraction layer for the kernel device driver + */ + +#include "mali_osk_locks.h" +#include "mali_kernel_common.h" +#include "mali_osk.h" + + +#ifdef DEBUG +#ifdef LOCK_ORDER_CHECKING +static DEFINE_SPINLOCK(lock_tracking_lock); +static mali_bool add_lock_to_log_and_check(struct _mali_osk_lock_debug_s *lock, uint32_t tid); +static void remove_lock_from_log(struct _mali_osk_lock_debug_s *lock, uint32_t tid); +static const char * const lock_order_to_string(_mali_osk_lock_order_t order); +#endif /* LOCK_ORDER_CHECKING */ + +void _mali_osk_locks_debug_init(struct _mali_osk_lock_debug_s *checker, _mali_osk_lock_flags_t flags, _mali_osk_lock_order_t order) +{ + checker->orig_flags = flags; + checker->owner = 0; + +#ifdef LOCK_ORDER_CHECKING + checker->order = order; + checker->next = NULL; +#endif +} + +void _mali_osk_locks_debug_add(struct _mali_osk_lock_debug_s *checker) +{ + checker->owner = _mali_osk_get_tid(); + +#ifdef LOCK_ORDER_CHECKING + if (!(checker->orig_flags & _MALI_OSK_LOCKFLAG_UNORDERED)) { + if (!add_lock_to_log_and_check(checker, _mali_osk_get_tid())) { + printk(KERN_ERR "%d: ERROR lock %p taken while holding a lock of a higher order.\n", + _mali_osk_get_tid(), checker); + dump_stack(); + } + } +#endif +} + +void _mali_osk_locks_debug_remove(struct _mali_osk_lock_debug_s *checker) +{ + +#ifdef LOCK_ORDER_CHECKING + if (!(checker->orig_flags & _MALI_OSK_LOCKFLAG_UNORDERED)) { + remove_lock_from_log(checker, _mali_osk_get_tid()); + } +#endif + checker->owner = 0; +} + + +#ifdef LOCK_ORDER_CHECKING +/* Lock order checking + * ------------------- + * + * To assure that lock ordering scheme defined by _mali_osk_lock_order_t is strictly adhered to, the + * following function will, together with a linked list and some extra members in _mali_osk_lock_debug_s, + * make sure that a lock that is taken has a higher order than the current highest-order lock a + * thread holds. + * + * This is done in the following manner: + * - A linked list keeps track of locks held by a thread. + * - A `next' pointer is added to each lock. This is used to chain the locks together. + * - When taking a lock, the `add_lock_to_log_and_check' makes sure that taking + * the given lock is legal. It will follow the linked list to find the last + * lock taken by this thread. If the last lock's order was lower than the + * lock that is to be taken, it appends the new lock to the list and returns + * true, if not, it return false. This return value is assert()'ed on in + * _mali_osk_lock_wait(). + */ + +static struct _mali_osk_lock_debug_s *lock_lookup_list; + +static void dump_lock_tracking_list(void) +{ + struct _mali_osk_lock_debug_s *l; + u32 n = 1; + + /* print list for debugging purposes */ + l = lock_lookup_list; + + while (NULL != l) { + printk(" [lock: %p, tid_owner: %d, order: %d] ->", l, l->owner, l->order); + l = l->next; + MALI_DEBUG_ASSERT(n++ < 100); + } + printk(" NULL\n"); +} + +static int tracking_list_length(void) +{ + struct _mali_osk_lock_debug_s *l; + u32 n = 0; + l = lock_lookup_list; + + while (NULL != l) { + l = l->next; + n++; + MALI_DEBUG_ASSERT(n < 100); + } + return n; +} + +static mali_bool add_lock_to_log_and_check(struct _mali_osk_lock_debug_s *lock, uint32_t tid) +{ + mali_bool ret = MALI_FALSE; + _mali_osk_lock_order_t highest_order_for_tid = _MALI_OSK_LOCK_ORDER_FIRST; + struct _mali_osk_lock_debug_s *highest_order_lock = (struct _mali_osk_lock_debug_s *)0xbeefbabe; + struct _mali_osk_lock_debug_s *l; + unsigned long local_lock_flag; + u32 len; + + spin_lock_irqsave(&lock_tracking_lock, local_lock_flag); + len = tracking_list_length(); + + l = lock_lookup_list; + if (NULL == l) { /* This is the first lock taken by this thread -- record and return true */ + lock_lookup_list = lock; + spin_unlock_irqrestore(&lock_tracking_lock, local_lock_flag); + return MALI_TRUE; + } else { + /* Traverse the locks taken and find the lock of the highest order. + * Since several threads may hold locks, each lock's owner must be + * checked so that locks not owned by this thread can be ignored. */ + for(;;) { + MALI_DEBUG_ASSERT_POINTER( l ); + if (tid == l->owner && l->order >= highest_order_for_tid) { + highest_order_for_tid = l->order; + highest_order_lock = l; + } + + if (NULL != l->next) { + l = l->next; + } else { + break; + } + } + + l->next = lock; + l->next = NULL; + } + + /* We have now found the highest order lock currently held by this thread and can see if it is + * legal to take the requested lock. */ + ret = highest_order_for_tid < lock->order; + + if (!ret) { + printk(KERN_ERR "Took lock of order %d (%s) while holding lock of order %d (%s)\n", + lock->order, lock_order_to_string(lock->order), + highest_order_for_tid, lock_order_to_string(highest_order_for_tid)); + dump_lock_tracking_list(); + } + + if (len+1 != tracking_list_length()) { + printk(KERN_ERR "************ lock: %p\n", lock); + printk(KERN_ERR "************ before: %d *** after: %d ****\n", len, tracking_list_length()); + dump_lock_tracking_list(); + MALI_DEBUG_ASSERT_POINTER(NULL); + } + + spin_unlock_irqrestore(&lock_tracking_lock, local_lock_flag); + return ret; +} + +static void remove_lock_from_log(struct _mali_osk_lock_debug_s *lock, uint32_t tid) +{ + struct _mali_osk_lock_debug_s *curr; + struct _mali_osk_lock_debug_s *prev = NULL; + unsigned long local_lock_flag; + u32 len; + u32 n = 0; + + spin_lock_irqsave(&lock_tracking_lock, local_lock_flag); + len = tracking_list_length(); + curr = lock_lookup_list; + + if (NULL == curr) { + printk(KERN_ERR "Error: Lock tracking list was empty on call to remove_lock_from_log\n"); + dump_lock_tracking_list(); + } + + MALI_DEBUG_ASSERT_POINTER(curr); + + + while (lock != curr) { + prev = curr; + + MALI_DEBUG_ASSERT_POINTER(curr); + curr = curr->next; + MALI_DEBUG_ASSERT(n++ < 100); + } + + if (NULL == prev) { + lock_lookup_list = curr->next; + } else { + MALI_DEBUG_ASSERT_POINTER(curr); + MALI_DEBUG_ASSERT_POINTER(prev); + prev->next = curr->next; + } + + lock->next = NULL; + + if (len-1 != tracking_list_length()) { + printk(KERN_ERR "************ lock: %p\n", lock); + printk(KERN_ERR "************ before: %d *** after: %d ****\n", len, tracking_list_length()); + dump_lock_tracking_list(); + MALI_DEBUG_ASSERT_POINTER(NULL); + } + + spin_unlock_irqrestore(&lock_tracking_lock, local_lock_flag); +} + +static const char * const lock_order_to_string(_mali_osk_lock_order_t order) +{ + switch (order) { + case _MALI_OSK_LOCK_ORDER_SESSIONS: + return "_MALI_OSK_LOCK_ORDER_SESSIONS"; + break; + case _MALI_OSK_LOCK_ORDER_MEM_SESSION: + return "_MALI_OSK_LOCK_ORDER_MEM_SESSION"; + break; + case _MALI_OSK_LOCK_ORDER_MEM_INFO: + return "_MALI_OSK_LOCK_ORDER_MEM_INFO"; + break; + case _MALI_OSK_LOCK_ORDER_MEM_PT_CACHE: + return "_MALI_OSK_LOCK_ORDER_MEM_PT_CACHE"; + break; + case _MALI_OSK_LOCK_ORDER_DESCRIPTOR_MAP: + return "_MALI_OSK_LOCK_ORDER_DESCRIPTOR_MAP"; + break; + case _MALI_OSK_LOCK_ORDER_GROUP_VIRTUAL: + return "_MALI_OSK_LOCK_ORDER_GROUP_VIRTUAL"; + break; + case _MALI_OSK_LOCK_ORDER_GROUP: + return "_MALI_OSK_LOCK_ORDER_GROUP"; + break; + case _MALI_OSK_LOCK_ORDER_SCHEDULER: + return "_MALI_OSK_LOCK_ORDER_SCHEDULER"; + break; + case _MALI_OSK_LOCK_ORDER_PM_CORE_STATE: + return "_MALI_OSK_LOCK_ORDER_PM_CORE_STATE"; + break; + case _MALI_OSK_LOCK_ORDER_L2_COMMAND: + return "_MALI_OSK_LOCK_ORDER_L2_COMMAND"; + break; + case _MALI_OSK_LOCK_ORDER_PROFILING: + return "_MALI_OSK_LOCK_ORDER_PROFILING"; + break; + case _MALI_OSK_LOCK_ORDER_L2_COUNTER: + return "_MALI_OSK_LOCK_ORDER_L2_COUNTER"; + break; + case _MALI_OSK_LOCK_ORDER_UTILIZATION: + return "_MALI_OSK_LOCK_ORDER_UTILIZATION"; + break; + case _MALI_OSK_LOCK_ORDER_PM_EXECUTE: + return "_MALI_OSK_LOCK_ORDER_PM_EXECUTE"; + break; + case _MALI_OSK_LOCK_ORDER_SESSION_PENDING_JOBS: + return "_MALI_OSK_LOCK_ORDER_SESSION_PENDING_JOBS"; + break; + default: + return ""; + } +} +#endif /* LOCK_ORDER_CHECKING */ +#endif /* DEBUG */ diff --git a/drivers/gpu/arm/mali/linux/mali_osk_locks.h b/drivers/gpu/arm/mali/linux/mali_osk_locks.h new file mode 100644 index 00000000000000..f5c45f7d391427 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_locks.h @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_locks.h + * Defines OS abstraction of lock and mutex + */ +#ifndef _MALI_OSK_LOCKS_H +#define _MALI_OSK_LOCKS_H + +#include +#include +#include + +#include + +#include "mali_osk_types.h" + +#ifdef _cplusplus +extern "C" { +#endif + + /* When DEBUG is enabled, this struct will be used to track owner, mode and order checking */ +#ifdef DEBUG + struct _mali_osk_lock_debug_s { + u32 owner; + _mali_osk_lock_flags_t orig_flags; + _mali_osk_lock_order_t order; + struct _mali_osk_lock_debug_s *next; + }; +#endif + + /* Anstraction of spinlock_t */ + struct _mali_osk_spinlock_s { +#ifdef DEBUG + struct _mali_osk_lock_debug_s checker; +#endif + spinlock_t spinlock; + }; + + /* Abstration of spinlock_t and lock flag which is used to store register's state before locking */ + struct _mali_osk_spinlock_irq_s { +#ifdef DEBUG + struct _mali_osk_lock_debug_s checker; +#endif + + spinlock_t spinlock; + unsigned long flags; + }; + + /* Abstraction of rw_semaphore in OS */ + struct _mali_osk_mutex_rw_s { +#ifdef DEBUG + struct _mali_osk_lock_debug_s checker; + _mali_osk_lock_mode_t mode; +#endif + + struct rw_semaphore rw_sema; + }; + + /* Mutex and mutex_interruptible functions share the same osk mutex struct */ + struct _mali_osk_mutex_s { +#ifdef DEBUG + struct _mali_osk_lock_debug_s checker; +#endif + struct mutex mutex; + }; + +#ifdef DEBUG + /** @brief _mali_osk_locks_debug_init/add/remove() functions are declared when DEBUG is enabled and + * defined in file mali_osk_locks.c. When LOCK_ORDER_CHECKING is enabled, calling these functions when we + * init/lock/unlock a lock/mutex, we could track lock order of a given tid. */ + void _mali_osk_locks_debug_init(struct _mali_osk_lock_debug_s *checker, _mali_osk_lock_flags_t flags, _mali_osk_lock_order_t order); + void _mali_osk_locks_debug_add(struct _mali_osk_lock_debug_s *checker); + void _mali_osk_locks_debug_remove(struct _mali_osk_lock_debug_s *checker); + + /** @brief This function can return a given lock's owner when DEBUG is enabled. */ + static inline u32 _mali_osk_lock_get_owner(struct _mali_osk_lock_debug_s *lock) + { + return lock->owner; + } +#else +#define _mali_osk_locks_debug_init(x, y, z) do {} while (0) +#define _mali_osk_locks_debug_add(x) do {} while (0) +#define _mali_osk_locks_debug_remove(x) do {} while (0) +#endif + + /** @brief Before use _mali_osk_spin_lock, init function should be used to allocate memory and initial spinlock*/ + static inline _mali_osk_spinlock_t *_mali_osk_spinlock_init(_mali_osk_lock_flags_t flags, _mali_osk_lock_order_t order) + { + _mali_osk_spinlock_t *lock = NULL; + + lock = kmalloc(sizeof(_mali_osk_spinlock_t), GFP_KERNEL); + if (NULL == lock) { + return NULL; + } + spin_lock_init(&lock->spinlock); + _mali_osk_locks_debug_init((struct _mali_osk_lock_debug_s *)lock, flags, order); + return lock; + } + + /** @brief Lock a spinlock */ + static inline void _mali_osk_spinlock_lock(_mali_osk_spinlock_t *lock) + { + BUG_ON(NULL == lock); + spin_lock(&lock->spinlock); + _mali_osk_locks_debug_add((struct _mali_osk_lock_debug_s *)lock); + } + + /** @brief Unlock a spinlock */ + static inline void _mali_osk_spinlock_unlock(_mali_osk_spinlock_t *lock) + { + BUG_ON(NULL == lock); + _mali_osk_locks_debug_remove((struct _mali_osk_lock_debug_s *)lock); + spin_unlock(&lock->spinlock); + } + + /** @brief Free a memory block which the argument lock pointed to and its type must be + * _mali_osk_spinlock_t *. */ + static inline void _mali_osk_spinlock_term(_mali_osk_spinlock_t *lock) + { + /* Parameter validation */ + BUG_ON(NULL == lock); + + /* Linux requires no explicit termination of spinlocks, semaphores, or rw_semaphores */ + kfree(lock); + } + + /** @brief Before _mali_osk_spinlock_irq_lock/unlock/term() is called, init function should be + * called to initial spinlock and flags in struct _mali_osk_spinlock_irq_t. */ + static inline _mali_osk_spinlock_irq_t *_mali_osk_spinlock_irq_init(_mali_osk_lock_flags_t flags, _mali_osk_lock_order_t order) + { + _mali_osk_spinlock_irq_t *lock = NULL; + lock = kmalloc(sizeof(_mali_osk_spinlock_irq_t), GFP_KERNEL); + + if (NULL == lock) { + return NULL; + } + + lock->flags = 0; + spin_lock_init(&lock->spinlock); + _mali_osk_locks_debug_init((struct _mali_osk_lock_debug_s *)lock, flags, order); + return lock; + } + + /** @brief Lock spinlock and save the register's state */ + static inline void _mali_osk_spinlock_irq_lock(_mali_osk_spinlock_irq_t *lock) + { + unsigned long tmp_flags; + + BUG_ON(NULL == lock); + spin_lock_irqsave(&lock->spinlock, tmp_flags); + lock->flags = tmp_flags; + _mali_osk_locks_debug_add((struct _mali_osk_lock_debug_s *)lock); + } + + /** @brief Unlock spinlock with saved register's state */ + static inline void _mali_osk_spinlock_irq_unlock(_mali_osk_spinlock_irq_t *lock) + { + BUG_ON(NULL == lock); + _mali_osk_locks_debug_remove((struct _mali_osk_lock_debug_s *)lock); + spin_unlock_irqrestore(&lock->spinlock, lock->flags); + } + + /** @brief Destroy a given memory block which lock pointed to, and the lock type must be + * _mali_osk_spinlock_irq_t *. */ + static inline void _mali_osk_spinlock_irq_term(_mali_osk_spinlock_irq_t *lock) + { + /* Parameter validation */ + BUG_ON(NULL == lock); + + /* Linux requires no explicit termination of spinlocks, semaphores, or rw_semaphores */ + kfree(lock); + } + + /** @brief Before _mali_osk_mutex_rw_wait/signal/term() is called, we should call + * _mali_osk_mutex_rw_init() to kmalloc a memory block and initial part of elements in it. */ + static inline _mali_osk_mutex_rw_t *_mali_osk_mutex_rw_init(_mali_osk_lock_flags_t flags, _mali_osk_lock_order_t order) + { + _mali_osk_mutex_rw_t *lock = NULL; + + lock = kmalloc(sizeof(_mali_osk_mutex_rw_t), GFP_KERNEL); + + if (NULL == lock) { + return NULL; + } + + init_rwsem(&lock->rw_sema); + _mali_osk_locks_debug_init((struct _mali_osk_lock_debug_s *)lock, flags, order); + return lock; + } + + /** @brief When call _mali_osk_mutex_rw_wait/signal() functions, the second argument mode + * should be assigned with value _MALI_OSK_LOCKMODE_RO or _MALI_OSK_LOCKMODE_RW */ + static inline void _mali_osk_mutex_rw_wait(_mali_osk_mutex_rw_t *lock, _mali_osk_lock_mode_t mode) + { + BUG_ON(NULL == lock); + BUG_ON(!(_MALI_OSK_LOCKMODE_RO == mode || _MALI_OSK_LOCKMODE_RW == mode)); + + if (mode == _MALI_OSK_LOCKMODE_RO) { + down_read(&lock->rw_sema); + } else { + down_write(&lock->rw_sema); + } + +#ifdef DEBUG + if (mode == _MALI_OSK_LOCKMODE_RW) { + lock->mode = mode; + } else { /* mode == _MALI_OSK_LOCKMODE_RO */ + lock->mode = mode; + } + _mali_osk_locks_debug_add((struct _mali_osk_lock_debug_s *)lock); +#endif + } + + /** @brief Up lock->rw_sema with up_read/write() accordinf argument mode's value. */ + static inline void _mali_osk_mutex_rw_signal(_mali_osk_mutex_rw_t *lock, _mali_osk_lock_mode_t mode) + { + BUG_ON(NULL == lock); + BUG_ON(!(_MALI_OSK_LOCKMODE_RO == mode || _MALI_OSK_LOCKMODE_RW == mode)); +#ifdef DEBUG + /* make sure the thread releasing the lock actually was the owner */ + if (mode == _MALI_OSK_LOCKMODE_RW) { + _mali_osk_locks_debug_remove((struct _mali_osk_lock_debug_s *)lock); + /* This lock now has no owner */ + lock->checker.owner = 0; + } +#endif + + if (mode == _MALI_OSK_LOCKMODE_RO) { + up_read(&lock->rw_sema); + } else { + up_write(&lock->rw_sema); + } + } + + /** @brief Free a given memory block which lock pointed to and its type must be + * _mali_sok_mutex_rw_t *. */ + static inline void _mali_osk_mutex_rw_term(_mali_osk_mutex_rw_t *lock) + { + /* Parameter validation */ + BUG_ON(NULL == lock); + + /* Linux requires no explicit termination of spinlocks, semaphores, or rw_semaphores */ + kfree(lock); + } + + /** @brief Mutex & mutex_interruptible share the same init and term function, because they have the + * same osk mutex struct, and the difference between them is which locking function they use */ + static inline _mali_osk_mutex_t *_mali_osk_mutex_init(_mali_osk_lock_flags_t flags, _mali_osk_lock_order_t order) + { + _mali_osk_mutex_t *lock = NULL; + + lock = kmalloc(sizeof(_mali_osk_mutex_t), GFP_KERNEL); + + if (NULL == lock) { + return NULL; + } + mutex_init(&lock->mutex); + + _mali_osk_locks_debug_init((struct _mali_osk_lock_debug_s *)lock, flags, order); + return lock; + } + + /** @brief Lock the lock->mutex with mutex_lock_interruptible function */ + static inline _mali_osk_errcode_t _mali_osk_mutex_wait_interruptible(_mali_osk_mutex_t *lock) + { + _mali_osk_errcode_t err = _MALI_OSK_ERR_OK; + + BUG_ON(NULL == lock); + + if (mutex_lock_interruptible(&lock->mutex)) { + printk(KERN_WARNING "Mali: Can not lock mutex\n"); + err = _MALI_OSK_ERR_RESTARTSYSCALL; + } + + _mali_osk_locks_debug_add((struct _mali_osk_lock_debug_s *)lock); + return err; + } + + /** @brief Unlock the lock->mutex which is locked with mutex_lock_interruptible() function. */ + static inline void _mali_osk_mutex_signal_interruptible(_mali_osk_mutex_t *lock) + { + BUG_ON(NULL == lock); + _mali_osk_locks_debug_remove((struct _mali_osk_lock_debug_s *)lock); + mutex_unlock(&lock->mutex); + } + + /** @brief Lock the lock->mutex just with mutex_lock() function which could not be interruptted. */ + static inline void _mali_osk_mutex_wait(_mali_osk_mutex_t *lock) + { + BUG_ON(NULL == lock); + mutex_lock(&lock->mutex); + _mali_osk_locks_debug_add((struct _mali_osk_lock_debug_s *)lock); + } + + /** @brief Unlock the lock->mutex which is locked with mutex_lock() function. */ + static inline void _mali_osk_mutex_signal(_mali_osk_mutex_t *lock) + { + BUG_ON(NULL == lock); + _mali_osk_locks_debug_remove((struct _mali_osk_lock_debug_s *)lock); + mutex_unlock(&lock->mutex); + } + + /** @brief Free a given memory block which lock point. */ + static inline void _mali_osk_mutex_term(_mali_osk_mutex_t *lock) + { + /* Parameter validation */ + BUG_ON(NULL == lock); + + /* Linux requires no explicit termination of spinlocks, semaphores, or rw_semaphores */ + kfree(lock); + } + +#ifdef _cplusplus +} +#endif + +#endif diff --git a/drivers/gpu/arm/mali/linux/mali_osk_low_level_mem.c b/drivers/gpu/arm/mali/linux/mali_osk_low_level_mem.c new file mode 100644 index 00000000000000..2fa7b1a011523a --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_low_level_mem.c @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_low_level_mem.c + * Implementation of the OS abstraction layer for the kernel device driver + */ + +#include +#include +#include + +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_ukk.h" + +void _mali_osk_mem_barrier( void ) +{ + mb(); +} + +void _mali_osk_write_mem_barrier( void ) +{ + wmb(); +} + +mali_io_address _mali_osk_mem_mapioregion( u32 phys, u32 size, const char *description ) +{ + return (mali_io_address)ioremap_nocache(phys, size); +} + +void _mali_osk_mem_unmapioregion( u32 phys, u32 size, mali_io_address virt ) +{ + iounmap((void*)virt); +} + +_mali_osk_errcode_t inline _mali_osk_mem_reqregion( u32 phys, u32 size, const char *description ) +{ +#if MALI_LICENSE_IS_GPL + return _MALI_OSK_ERR_OK; /* GPL driver gets the mem region for the resources registered automatically */ +#else + return ((NULL == request_mem_region(phys, size, description)) ? _MALI_OSK_ERR_NOMEM : _MALI_OSK_ERR_OK); +#endif +} + +void inline _mali_osk_mem_unreqregion( u32 phys, u32 size ) +{ +#if !MALI_LICENSE_IS_GPL + release_mem_region(phys, size); +#endif +} + +void inline _mali_osk_mem_iowrite32_relaxed( volatile mali_io_address addr, u32 offset, u32 val ) +{ + __raw_writel(cpu_to_le32(val),((u8*)addr) + offset); +} + +u32 inline _mali_osk_mem_ioread32( volatile mali_io_address addr, u32 offset ) +{ + return ioread32(((u8*)addr) + offset); +} + +void inline _mali_osk_mem_iowrite32( volatile mali_io_address addr, u32 offset, u32 val ) +{ + iowrite32(val, ((u8*)addr) + offset); +} + +void _mali_osk_cache_flushall( void ) +{ + /** @note Cached memory is not currently supported in this implementation */ +} + +void _mali_osk_cache_ensure_uncached_range_flushed( void *uncached_mapping, u32 offset, u32 size ) +{ + _mali_osk_write_mem_barrier(); +} + +u32 _mali_osk_mem_write_safe(void *dest, const void *src, u32 size) +{ +#define MALI_MEM_SAFE_COPY_BLOCK_SIZE 4096 + u32 retval = 0; + void *temp_buf; + + temp_buf = kmalloc(MALI_MEM_SAFE_COPY_BLOCK_SIZE, GFP_KERNEL); + if (NULL != temp_buf) { + u32 bytes_left_to_copy = size; + u32 i; + for (i = 0; i < size; i += MALI_MEM_SAFE_COPY_BLOCK_SIZE) { + u32 size_to_copy; + u32 size_copied; + u32 bytes_left; + + if (bytes_left_to_copy > MALI_MEM_SAFE_COPY_BLOCK_SIZE) { + size_to_copy = MALI_MEM_SAFE_COPY_BLOCK_SIZE; + } else { + size_to_copy = bytes_left_to_copy; + } + + bytes_left = copy_from_user(temp_buf, ((char*)src) + i, size_to_copy); + size_copied = size_to_copy - bytes_left; + + bytes_left = copy_to_user(((char*)dest) + i, temp_buf, size_copied); + size_copied -= bytes_left; + + bytes_left_to_copy -= size_copied; + retval += size_copied; + + if (size_copied != size_to_copy) { + break; /* Early out, we was not able to copy this entire block */ + } + } + + kfree(temp_buf); + } + + return retval; +} + +_mali_osk_errcode_t _mali_ukk_mem_write_safe(_mali_uk_mem_write_safe_s *args) +{ + MALI_DEBUG_ASSERT_POINTER(args); + + if (NULL == args->ctx) { + return _MALI_OSK_ERR_INVALID_ARGS; + } + + /* Return number of bytes actually copied */ + args->size = _mali_osk_mem_write_safe(args->dest, args->src, args->size); + return _MALI_OSK_ERR_OK; +} diff --git a/drivers/gpu/arm/mali/linux/mali_osk_mali.c b/drivers/gpu/arm/mali/linux/mali_osk_mali.c new file mode 100644 index 00000000000000..626c35fd0e11d5 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_mali.c @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_mali.c + * Implementation of the OS abstraction layer which is specific for the Mali kernel device driver + */ +#include +#include +#include +#include + +#include "mali_osk_mali.h" +#include "mali_kernel_common.h" /* MALI_xxx macros */ +#include "mali_osk.h" /* kernel side OS functions */ +#include "mali_kernel_linux.h" + +_mali_osk_errcode_t _mali_osk_resource_find(u32 addr, _mali_osk_resource_t *res) +{ + int i; + + if (NULL == mali_platform_device) { + /* Not connected to a device */ + return _MALI_OSK_ERR_ITEM_NOT_FOUND; + } + + for (i = 0; i < mali_platform_device->num_resources; i++) { + if (IORESOURCE_MEM == resource_type(&(mali_platform_device->resource[i])) && + mali_platform_device->resource[i].start == addr) { + if (NULL != res) { + res->base = addr; + res->description = mali_platform_device->resource[i].name; + + /* Any (optional) IRQ resource belonging to this resource will follow */ + if ((i + 1) < mali_platform_device->num_resources && + IORESOURCE_IRQ == resource_type(&(mali_platform_device->resource[i+1]))) { + res->irq = mali_platform_device->resource[i+1].start; + } else { + res->irq = -1; + } + } + return _MALI_OSK_ERR_OK; + } + } + + return _MALI_OSK_ERR_ITEM_NOT_FOUND; +} + +u32 _mali_osk_resource_base_address(void) +{ + u32 lowest_addr = 0xFFFFFFFF; + u32 ret = 0; + + if (NULL != mali_platform_device) { + int i; + for (i = 0; i < mali_platform_device->num_resources; i++) { + if (mali_platform_device->resource[i].flags & IORESOURCE_MEM && + mali_platform_device->resource[i].start < lowest_addr) { + lowest_addr = mali_platform_device->resource[i].start; + ret = lowest_addr; + } + } + } + + return ret; +} + +_mali_osk_errcode_t _mali_osk_device_data_get(struct _mali_osk_device_data *data) +{ + MALI_DEBUG_ASSERT_POINTER(data); + + if (NULL != mali_platform_device) { + struct mali_gpu_device_data* os_data = NULL; + + os_data = (struct mali_gpu_device_data*)mali_platform_device->dev.platform_data; + if (NULL != os_data) { + /* Copy data from OS dependant struct to Mali neutral struct (identical!) */ + data->dedicated_mem_start = os_data->dedicated_mem_start; + data->dedicated_mem_size = os_data->dedicated_mem_size; + data->shared_mem_size = os_data->shared_mem_size; + data->fb_start = os_data->fb_start; + data->fb_size = os_data->fb_size; + data->max_job_runtime = os_data->max_job_runtime; + data->utilization_interval = os_data->utilization_interval; + data->utilization_callback = os_data->utilization_callback; + data->pmu_switch_delay = os_data->pmu_switch_delay; + data->set_freq_callback = os_data->set_freq_callback; + + memcpy(data->pmu_domain_config, os_data->pmu_domain_config, sizeof(os_data->pmu_domain_config)); + return _MALI_OSK_ERR_OK; + } + } + + return _MALI_OSK_ERR_ITEM_NOT_FOUND; +} + +mali_bool _mali_osk_shared_interrupts(void) +{ + u32 irqs[128]; + u32 i, j, irq, num_irqs_found = 0; + + MALI_DEBUG_ASSERT_POINTER(mali_platform_device); + MALI_DEBUG_ASSERT(128 >= mali_platform_device->num_resources); + + for (i = 0; i < mali_platform_device->num_resources; i++) { + if (IORESOURCE_IRQ & mali_platform_device->resource[i].flags) { + irq = mali_platform_device->resource[i].start; + + for (j = 0; j < num_irqs_found; ++j) { + if (irq == irqs[j]) { + return MALI_TRUE; + } + } + + irqs[num_irqs_found++] = irq; + } + } + + return MALI_FALSE; +} diff --git a/drivers/gpu/arm/mali/linux/mali_osk_math.c b/drivers/gpu/arm/mali/linux/mali_osk_math.c new file mode 100644 index 00000000000000..c097fcc774218d --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_math.c @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2010, 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_math.c + * Implementation of the OS abstraction layer for the kernel device driver + */ + +#include "mali_osk.h" +#include + +u32 _mali_osk_clz( u32 input ) +{ + return 32-fls(input); +} + +u32 _mali_osk_fls( u32 input ) +{ + return fls(input); +} diff --git a/drivers/gpu/arm/mali/linux/mali_osk_memory.c b/drivers/gpu/arm/mali/linux/mali_osk_memory.c new file mode 100644 index 00000000000000..64d8951709f0b3 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_memory.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010-2011, 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_memory.c + * Implementation of the OS abstraction layer for the kernel device driver + */ + +#include "mali_osk.h" +#include +#include + +void inline *_mali_osk_calloc( u32 n, u32 size ) +{ + return kcalloc(n, size, GFP_KERNEL); +} + +void inline *_mali_osk_malloc( u32 size ) +{ + return kmalloc(size, GFP_KERNEL); +} + +void inline _mali_osk_free( void *ptr ) +{ + kfree(ptr); +} + +void inline *_mali_osk_valloc( u32 size ) +{ + return vmalloc(size); +} + +void inline _mali_osk_vfree( void *ptr ) +{ + vfree(ptr); +} + +void inline *_mali_osk_memcpy( void *dst, const void *src, u32 len ) +{ + return memcpy(dst, src, len); +} + +void inline *_mali_osk_memset( void *s, u32 c, u32 n ) +{ + return memset(s, c, n); +} + +mali_bool _mali_osk_mem_check_allocated( u32 max_allocated ) +{ + /* No need to prevent an out-of-memory dialogue appearing on Linux, + * so we always return MALI_TRUE. + */ + return MALI_TRUE; +} diff --git a/drivers/gpu/arm/mali/linux/mali_osk_misc.c b/drivers/gpu/arm/mali/linux/mali_osk_misc.c new file mode 100644 index 00000000000000..28ea90a03ba8a8 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_misc.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_misc.c + * Implementation of the OS abstraction layer for the kernel device driver + */ +#include +#include +#include +#include +#include +#include "mali_osk.h" + +void _mali_osk_dbgmsg( const char *fmt, ... ) +{ + va_list args; + va_start(args, fmt); + vprintk(fmt, args); + va_end(args); +} + +u32 _mali_osk_snprintf( char *buf, u32 size, const char *fmt, ... ) +{ + int res; + va_list args; + va_start(args, fmt); + + res = vscnprintf(buf, (size_t)size, fmt, args); + + va_end(args); + return res; +} + +void _mali_osk_abort(void) +{ + /* make a simple fault by dereferencing a NULL pointer */ + dump_stack(); + *(int *)0 = 0; +} + +void _mali_osk_break(void) +{ + _mali_osk_abort(); +} + +u32 _mali_osk_get_pid(void) +{ + /* Thread group ID is the process ID on Linux */ + return (u32)current->tgid; +} + +u32 _mali_osk_get_tid(void) +{ + /* pid is actually identifying the thread on Linux */ + u32 tid = current->pid; + + /* If the pid is 0 the core was idle. Instead of returning 0 we return a special number + * identifying which core we are on. */ + if (0 == tid) { + tid = -(1 + raw_smp_processor_id()); + } + + return tid; +} diff --git a/drivers/gpu/arm/mali/linux/mali_osk_notification.c b/drivers/gpu/arm/mali/linux/mali_osk_notification.c new file mode 100644 index 00000000000000..3738ad35260772 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_notification.c @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_notification.c + * Implementation of the OS abstraction layer for the kernel device driver + */ + +#include "mali_osk.h" +#include "mali_kernel_common.h" + +#include +#include +#include + +/** + * Declaration of the notification queue object type + * Contains a linked list of notification pending delivery to user space. + * It also contains a wait queue of exclusive waiters blocked in the ioctl + * When a new notification is posted a single thread is resumed. + */ +struct _mali_osk_notification_queue_t_struct { + spinlock_t mutex; /**< Mutex protecting the list */ + wait_queue_head_t receive_queue; /**< Threads waiting for new entries to the queue */ + struct list_head head; /**< List of notifications waiting to be picked up */ +}; + +typedef struct _mali_osk_notification_wrapper_t_struct { + struct list_head list; /**< Internal linked list variable */ + _mali_osk_notification_t data; /**< Notification data */ +} _mali_osk_notification_wrapper_t; + +_mali_osk_notification_queue_t *_mali_osk_notification_queue_init( void ) +{ + _mali_osk_notification_queue_t * result; + + result = (_mali_osk_notification_queue_t *)kmalloc(sizeof(_mali_osk_notification_queue_t), GFP_KERNEL); + if (NULL == result) return NULL; + + spin_lock_init(&result->mutex); + init_waitqueue_head(&result->receive_queue); + INIT_LIST_HEAD(&result->head); + + return result; +} + +_mali_osk_notification_t *_mali_osk_notification_create( u32 type, u32 size ) +{ + /* OPT Recycling of notification objects */ + _mali_osk_notification_wrapper_t *notification; + + notification = (_mali_osk_notification_wrapper_t *)kmalloc( sizeof(_mali_osk_notification_wrapper_t) + size, + GFP_KERNEL | __GFP_HIGH | __GFP_REPEAT); + if (NULL == notification) { + MALI_DEBUG_PRINT(1, ("Failed to create a notification object\n")); + return NULL; + } + + /* Init the list */ + INIT_LIST_HEAD(¬ification->list); + + if (0 != size) { + notification->data.result_buffer = ((u8*)notification) + sizeof(_mali_osk_notification_wrapper_t); + } else { + notification->data.result_buffer = NULL; + } + + /* set up the non-allocating fields */ + notification->data.notification_type = type; + notification->data.result_buffer_size = size; + + /* all ok */ + return &(notification->data); +} + +void _mali_osk_notification_delete( _mali_osk_notification_t *object ) +{ + _mali_osk_notification_wrapper_t *notification; + MALI_DEBUG_ASSERT_POINTER( object ); + + notification = container_of( object, _mali_osk_notification_wrapper_t, data ); + + /* Free the container */ + kfree(notification); +} + +void _mali_osk_notification_queue_term( _mali_osk_notification_queue_t *queue ) +{ + _mali_osk_notification_t *result; + MALI_DEBUG_ASSERT_POINTER( queue ); + + while (_MALI_OSK_ERR_OK == _mali_osk_notification_queue_dequeue(queue, &result)) { + _mali_osk_notification_delete( result ); + } + + /* not much to do, just free the memory */ + kfree(queue); +} +void _mali_osk_notification_queue_send( _mali_osk_notification_queue_t *queue, _mali_osk_notification_t *object ) +{ +#if defined(MALI_UPPER_HALF_SCHEDULING) + unsigned long irq_flags; +#endif + + _mali_osk_notification_wrapper_t *notification; + MALI_DEBUG_ASSERT_POINTER( queue ); + MALI_DEBUG_ASSERT_POINTER( object ); + + notification = container_of( object, _mali_osk_notification_wrapper_t, data ); + +#if defined(MALI_UPPER_HALF_SCHEDULING) + spin_lock_irqsave(&queue->mutex, irq_flags); +#else + spin_lock(&queue->mutex); +#endif + + list_add_tail(¬ification->list, &queue->head); + +#if defined(MALI_UPPER_HALF_SCHEDULING) + spin_unlock_irqrestore(&queue->mutex, irq_flags); +#else + spin_unlock(&queue->mutex); +#endif + + /* and wake up one possible exclusive waiter */ + wake_up(&queue->receive_queue); +} + +_mali_osk_errcode_t _mali_osk_notification_queue_dequeue( _mali_osk_notification_queue_t *queue, _mali_osk_notification_t **result ) +{ +#if defined(MALI_UPPER_HALF_SCHEDULING) + unsigned long irq_flags; +#endif + + _mali_osk_errcode_t ret = _MALI_OSK_ERR_ITEM_NOT_FOUND; + _mali_osk_notification_wrapper_t *wrapper_object; + +#if defined(MALI_UPPER_HALF_SCHEDULING) + spin_lock_irqsave(&queue->mutex, irq_flags); +#else + spin_lock(&queue->mutex); +#endif + + if (!list_empty(&queue->head)) { + wrapper_object = list_entry(queue->head.next, _mali_osk_notification_wrapper_t, list); + *result = &(wrapper_object->data); + list_del_init(&wrapper_object->list); + ret = _MALI_OSK_ERR_OK; + } + +#if defined(MALI_UPPER_HALF_SCHEDULING) + spin_unlock_irqrestore(&queue->mutex, irq_flags); +#else + spin_unlock(&queue->mutex); +#endif + + return ret; +} + +_mali_osk_errcode_t _mali_osk_notification_queue_receive( _mali_osk_notification_queue_t *queue, _mali_osk_notification_t **result ) +{ + /* check input */ + MALI_DEBUG_ASSERT_POINTER( queue ); + MALI_DEBUG_ASSERT_POINTER( result ); + + /* default result */ + *result = NULL; + + if (wait_event_interruptible(queue->receive_queue, + _MALI_OSK_ERR_OK == _mali_osk_notification_queue_dequeue(queue, result))) { + return _MALI_OSK_ERR_RESTARTSYSCALL; + } + + return _MALI_OSK_ERR_OK; /* all ok */ +} diff --git a/drivers/gpu/arm/mali/linux/mali_osk_pm.c b/drivers/gpu/arm/mali/linux/mali_osk_pm.c new file mode 100644 index 00000000000000..f8769b189a09cf --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_pm.c @@ -0,0 +1,109 @@ +/** + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_pm.c + * Implementation of the callback functions from common power management + */ + +#include + +#ifdef CONFIG_PM_RUNTIME +#include +#endif /* CONFIG_PM_RUNTIME */ +#include +#include +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_kernel_linux.h" + +static _mali_osk_atomic_t mali_pm_ref_count; + +void _mali_osk_pm_dev_enable(void) +{ + _mali_osk_atomic_init(&mali_pm_ref_count, 0); +} + +void _mali_osk_pm_dev_disable(void) +{ + _mali_osk_atomic_term(&mali_pm_ref_count); +} + +/* Can NOT run in atomic context */ +_mali_osk_errcode_t _mali_osk_pm_dev_ref_add(void) +{ +#ifdef CONFIG_PM_RUNTIME + int err; + MALI_DEBUG_ASSERT_POINTER(mali_platform_device); + err = pm_runtime_get_sync(&(mali_platform_device->dev)); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) + pm_runtime_mark_last_busy(&(mali_platform_device->dev)); +#endif + if (0 > err) { + MALI_PRINT_ERROR(("Mali OSK PM: pm_runtime_get_sync() returned error code %d\n", err)); + return _MALI_OSK_ERR_FAULT; + } + _mali_osk_atomic_inc(&mali_pm_ref_count); + MALI_DEBUG_PRINT(4, ("Mali OSK PM: Power ref taken (%u)\n", _mali_osk_atomic_read(&mali_pm_ref_count))); +#endif + return _MALI_OSK_ERR_OK; +} + +/* Can run in atomic context */ +void _mali_osk_pm_dev_ref_dec(void) +{ +#ifdef CONFIG_PM_RUNTIME + MALI_DEBUG_ASSERT_POINTER(mali_platform_device); + _mali_osk_atomic_dec(&mali_pm_ref_count); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) + pm_runtime_mark_last_busy(&(mali_platform_device->dev)); + pm_runtime_put_autosuspend(&(mali_platform_device->dev)); +#else + pm_runtime_put(&(mali_platform_device->dev)); +#endif + MALI_DEBUG_PRINT(4, ("Mali OSK PM: Power ref released (%u)\n", _mali_osk_atomic_read(&mali_pm_ref_count))); +#endif +} + +/* Can run in atomic context */ +mali_bool _mali_osk_pm_dev_ref_add_no_power_on(void) +{ +#ifdef CONFIG_PM_RUNTIME + u32 ref; + MALI_DEBUG_ASSERT_POINTER(mali_platform_device); + pm_runtime_get_noresume(&(mali_platform_device->dev)); + ref = _mali_osk_atomic_read(&mali_pm_ref_count); + MALI_DEBUG_PRINT(4, ("Mali OSK PM: No-power ref taken (%u)\n", _mali_osk_atomic_read(&mali_pm_ref_count))); + return ref > 0 ? MALI_TRUE : MALI_FALSE; +#else + return MALI_TRUE; +#endif +} + +/* Can run in atomic context */ +void _mali_osk_pm_dev_ref_dec_no_power_on(void) +{ +#ifdef CONFIG_PM_RUNTIME + MALI_DEBUG_ASSERT_POINTER(mali_platform_device); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) + pm_runtime_put_autosuspend(&(mali_platform_device->dev)); +#else + pm_runtime_put(&(mali_platform_device->dev)); +#endif + MALI_DEBUG_PRINT(4, ("Mali OSK PM: No-power ref released (%u)\n", _mali_osk_atomic_read(&mali_pm_ref_count))); +#endif +} + +void _mali_osk_pm_dev_barrier(void) +{ +#ifdef CONFIG_PM_RUNTIME + pm_runtime_barrier(&(mali_platform_device->dev)); +#endif +} diff --git a/drivers/gpu/arm/mali/linux/mali_osk_profiling.c b/drivers/gpu/arm/mali/linux/mali_osk_profiling.c new file mode 100644 index 00000000000000..0c1ff3b7f213cc --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_profiling.c @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include + +#include +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_ukk.h" +#include "mali_uk_types.h" +#include "mali_osk_profiling.h" +#include "mali_linux_trace.h" +#include "mali_gp.h" +#include "mali_pp.h" +#include "mali_pp_scheduler.h" +#include "mali_l2_cache.h" +#include "mali_user_settings_db.h" + +_mali_osk_errcode_t _mali_osk_profiling_init(mali_bool auto_start) +{ + if (MALI_TRUE == auto_start) { + mali_set_user_setting(_MALI_UK_USER_SETTING_SW_EVENTS_ENABLE, MALI_TRUE); + } + + return _MALI_OSK_ERR_OK; +} + +void _mali_osk_profiling_term(void) +{ + /* Nothing to do */ +} + +_mali_osk_errcode_t _mali_osk_profiling_start(u32 * limit) +{ + /* Nothing to do */ + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t _mali_osk_profiling_stop(u32 *count) +{ + /* Nothing to do */ + return _MALI_OSK_ERR_OK; +} + +u32 _mali_osk_profiling_get_count(void) +{ + return 0; +} + +_mali_osk_errcode_t _mali_osk_profiling_get_event(u32 index, u64* timestamp, u32* event_id, u32 data[5]) +{ + /* Nothing to do */ + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t _mali_osk_profiling_clear(void) +{ + /* Nothing to do */ + return _MALI_OSK_ERR_OK; +} + +mali_bool _mali_osk_profiling_is_recording(void) +{ + return MALI_FALSE; +} + +mali_bool _mali_osk_profiling_have_recording(void) +{ + return MALI_FALSE; +} + +void _mali_osk_profiling_report_sw_counters(u32 *counters) +{ + trace_mali_sw_counters(_mali_osk_get_pid(), _mali_osk_get_tid(), NULL, counters); +} + + +_mali_osk_errcode_t _mali_ukk_profiling_start(_mali_uk_profiling_start_s *args) +{ + return _mali_osk_profiling_start(&args->limit); +} + +_mali_osk_errcode_t _mali_ukk_profiling_add_event(_mali_uk_profiling_add_event_s *args) +{ + /* Always add process and thread identificator in the first two data elements for events from user space */ + _mali_osk_profiling_add_event(args->event_id, _mali_osk_get_pid(), _mali_osk_get_tid(), args->data[2], args->data[3], args->data[4]); + + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t _mali_ukk_profiling_stop(_mali_uk_profiling_stop_s *args) +{ + return _mali_osk_profiling_stop(&args->count); +} + +_mali_osk_errcode_t _mali_ukk_profiling_get_event(_mali_uk_profiling_get_event_s *args) +{ + return _mali_osk_profiling_get_event(args->index, &args->timestamp, &args->event_id, args->data); +} + +_mali_osk_errcode_t _mali_ukk_profiling_clear(_mali_uk_profiling_clear_s *args) +{ + return _mali_osk_profiling_clear(); +} + +_mali_osk_errcode_t _mali_ukk_sw_counters_report(_mali_uk_sw_counters_report_s *args) +{ + _mali_osk_profiling_report_sw_counters(args->counters); + return _MALI_OSK_ERR_OK; +} + +/** + * Called by gator.ko to set HW counters + * + * @param counter_id The counter ID. + * @param event_id Event ID that the counter should count (HW counter value from TRM). + * + * @return 1 on success, 0 on failure. + */ +int _mali_profiling_set_event(u32 counter_id, s32 event_id) +{ + if (COUNTER_VP_0_C0 == counter_id) { + mali_gp_job_set_gp_counter_src0(event_id); + } else if (COUNTER_VP_0_C1 == counter_id) { + mali_gp_job_set_gp_counter_src1(event_id); + } else if (COUNTER_FP_0_C0 <= counter_id && COUNTER_FP_7_C1 >= counter_id) { + /* + * Two compatibility notes for this function: + * + * 1) Previously the DDK allowed per core counters. + * + * This did not make much sense on Mali-450 with the "virtual PP core" concept, + * so this option was removed, and only the same pair of HW counters was allowed on all cores, + * beginning with r3p2 release. + * + * Starting with r4p0, it is now possible to set different HW counters for the different sub jobs. + * This should be almost the same, since sub job 0 is designed to run on core 0, + * sub job 1 on core 1, and so on. + * + * The scheduling of PP sub jobs is not predictable, and this often led to situations where core 0 ran 2 + * sub jobs, while for instance core 1 ran zero. Having the counters set per sub job would thus increase + * the predictability of the returned data (as you would be guaranteed data for all the selected HW counters). + * + * PS: Core scaling needs to be disabled in order to use this reliably (goes for both solutions). + * + * The framework/#defines with Gator still indicates that the counter is for a particular core, + * but this is internally used as a sub job ID instead (no translation needed). + * + * 2) Global/default vs per sub job counters + * + * Releases before r3p2 had only per PP core counters. + * r3p2 releases had only one set of default/global counters which applied to all PP cores + * Starting with r4p0, we have both a set of default/global counters, + * and individual counters per sub job (equal to per core). + * + * To keep compatibility with Gator/DS-5/streamline, the following scheme is used: + * + * r3p2 release; only counters set for core 0 is handled, + * this is applied as the default/global set of counters, and will thus affect all cores. + * + * r4p0 release; counters set for core 0 is applied as both the global/default set of counters, + * and counters for sub job 0. + * Counters set for core 1-7 is only applied for the corresponding sub job. + * + * This should allow the DS-5/Streamline GUI to have a simple mode where it only allows setting the + * values for core 0, and thus this will be applied to all PP sub jobs/cores. + * Advanced mode will also be supported, where individual pairs of HW counters can be selected. + * + * The GUI will (until it is updated) still refer to cores instead of sub jobs, but this is probably + * something we can live with! + * + * Mali-450 note: Each job is not divided into a deterministic number of sub jobs, as the HW DLBU + * automatically distributes the load between whatever number of cores is available at this particular time. + * A normal PP job on Mali-450 is thus considered a single (virtual) job, and it will thus only be possible + * to use a single pair of HW counters (even if the job ran on multiple PP cores). + * In other words, only the global/default pair of PP HW counters will be used for normal Mali-450 jobs. + */ + u32 sub_job = (counter_id - COUNTER_FP_0_C0) >> 1; + u32 counter_src = (counter_id - COUNTER_FP_0_C0) & 1; + if (0 == counter_src) { + mali_pp_job_set_pp_counter_sub_job_src0(sub_job, event_id); + if (0 == sub_job) { + mali_pp_job_set_pp_counter_global_src0(event_id); + } + } else { + mali_pp_job_set_pp_counter_sub_job_src1(sub_job, event_id); + if (0 == sub_job) { + mali_pp_job_set_pp_counter_global_src1(event_id); + } + } + } else if (COUNTER_L2_0_C0 <= counter_id && COUNTER_L2_2_C1 >= counter_id) { + u32 core_id = (counter_id - COUNTER_L2_0_C0) >> 1; + struct mali_l2_cache_core* l2_cache_core = mali_l2_cache_core_get_glob_l2_core(core_id); + + if (NULL != l2_cache_core) { + u32 counter_src = (counter_id - COUNTER_L2_0_C0) & 1; + if (0 == counter_src) { + mali_l2_cache_core_set_counter_src0(l2_cache_core, event_id); + } else { + mali_l2_cache_core_set_counter_src1(l2_cache_core, event_id); + } + } + } else { + return 0; /* Failure, unknown event */ + } + + return 1; /* success */ +} + +/** + * Called by gator.ko to retrieve the L2 cache counter values for all L2 cache cores. + * The L2 cache counters are unique in that they are polled by gator, rather than being + * transmitted via the tracepoint mechanism. + * + * @param values Pointer to a _mali_profiling_l2_counter_values structure where + * the counter sources and values will be output + * @return 0 if all went well; otherwise, return the mask with the bits set for the powered off cores + */ +u32 _mali_profiling_get_l2_counters(_mali_profiling_l2_counter_values *values) +{ + struct mali_l2_cache_core *l2_cache; + u32 l2_cores_num = mali_l2_cache_core_get_glob_num_l2_cores(); + u32 i; + u32 ret = 0; + + MALI_DEBUG_ASSERT(l2_cores_num <= 3); + + for (i = 0; i < l2_cores_num; i++) { + l2_cache = mali_l2_cache_core_get_glob_l2_core(i); + + if (NULL == l2_cache) { + continue; + } + + if (MALI_TRUE == mali_l2_cache_lock_power_state(l2_cache)) { + /* It is now safe to access the L2 cache core in order to retrieve the counters */ + mali_l2_cache_core_get_counter_values(l2_cache, + &values->cores[i].source0, + &values->cores[i].value0, + &values->cores[i].source1, + &values->cores[i].value1); + } else { + /* The core was not available, set the right bit in the mask. */ + ret |= (1 << i); + } + mali_l2_cache_unlock_power_state(l2_cache); + } + + return ret; +} + +/** + * Called by gator to control the production of profiling information at runtime. + */ +void _mali_profiling_control(u32 action, u32 value) +{ + switch(action) { + case FBDUMP_CONTROL_ENABLE: + mali_set_user_setting(_MALI_UK_USER_SETTING_COLORBUFFER_CAPTURE_ENABLED, (value == 0 ? MALI_FALSE : MALI_TRUE)); + break; + case FBDUMP_CONTROL_RATE: + mali_set_user_setting(_MALI_UK_USER_SETTING_BUFFER_CAPTURE_N_FRAMES, value); + break; + case SW_COUNTER_ENABLE: + mali_set_user_setting(_MALI_UK_USER_SETTING_SW_COUNTER_ENABLED, value); + break; + case FBDUMP_CONTROL_RESIZE_FACTOR: + mali_set_user_setting(_MALI_UK_USER_SETTING_BUFFER_CAPTURE_RESIZE_FACTOR, value); + break; + default: + break; /* Ignore unimplemented actions */ + } +} + +/** + * Called by gator to get mali api version. + */ +u32 _mali_profiling_get_api_version(void) +{ + return MALI_PROFILING_API_VERSION; +} + +/** +* Called by gator to get the data about Mali instance in use: +* product id, version, number of cores +*/ +void _mali_profiling_get_mali_version(struct _mali_profiling_mali_version *values) +{ + values->mali_product_id = (u32)mali_kernel_core_get_product_id(); + values->mali_version_major = mali_kernel_core_get_gpu_major_version(); + values->mali_version_minor = mali_kernel_core_get_gpu_minor_version(); + values->num_of_l2_cores = mali_l2_cache_core_get_glob_num_l2_cores(); + values->num_of_fp_cores = mali_pp_scheduler_get_num_cores_total(); + values->num_of_vp_cores = 1; +} + +EXPORT_SYMBOL(_mali_profiling_set_event); +EXPORT_SYMBOL(_mali_profiling_get_l2_counters); +EXPORT_SYMBOL(_mali_profiling_control); +EXPORT_SYMBOL(_mali_profiling_get_api_version); +EXPORT_SYMBOL(_mali_profiling_get_mali_version); diff --git a/drivers/gpu/arm/mali/linux/mali_osk_specific.h b/drivers/gpu/arm/mali/linux/mali_osk_specific.h new file mode 100644 index 00000000000000..eca873f19dc2d4 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_specific.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2010, 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_specific.h + * Defines per-OS Kernel level specifics, such as unusual workarounds for + * certain OSs. + */ + +#ifndef __MALI_OSK_SPECIFIC_H__ +#define __MALI_OSK_SPECIFIC_H__ + +#include +#include +#include +#include +#include + +#include "mali_osk_types.h" +#include "mali_kernel_linux.h" + +#define MALI_STATIC_INLINE static inline +#define MALI_NON_STATIC_INLINE inline + +typedef struct dma_pool * mali_dma_pool; + + +MALI_STATIC_INLINE mali_dma_pool mali_dma_pool_create(u32 size, u32 alignment, u32 boundary) +{ + return dma_pool_create("mali-dma", &mali_platform_device->dev, size, alignment, boundary); +} + +MALI_STATIC_INLINE void mali_dma_pool_destroy(mali_dma_pool pool) +{ + dma_pool_destroy(pool); +} + +MALI_STATIC_INLINE mali_io_address mali_dma_pool_alloc(mali_dma_pool pool, u32 *phys_addr) +{ + return dma_pool_alloc(pool, GFP_KERNEL, phys_addr); +} + +MALI_STATIC_INLINE void mali_dma_pool_free(mali_dma_pool pool, void* virt_addr, u32 phys_addr) +{ + dma_pool_free(pool, virt_addr, phys_addr); +} + + +#if MALI_ENABLE_CPU_CYCLES +/* Reads out the clock cycle performance counter of the current cpu. + It is useful for cost-free (2 cycle) measuring of the time spent + in a code path. Sample before and after, the diff number of cycles. + When the CPU is idle it will not increase this clock counter. + It means that the counter is accurate if only spin-locks are used, + but mutexes may lead to too low values since the cpu might "idle" + waiting for the mutex to become available. + The clock source is configured on the CPU during mali module load, + but will not give useful output after a CPU has been power cycled. + It is therefore important to configure the system to not turn of + the cpu cores when using this functionallity.*/ +static inline unsigned int mali_get_cpu_cyclecount(void) +{ + unsigned int value; + /* Reading the CCNT Register - CPU clock counter */ + asm volatile ("MRC p15, 0, %0, c9, c13, 0\t\n": "=r"(value)); + return value; +} + +void mali_init_cpu_time_counters(int reset, int enable_divide_by_64); +#endif + + +MALI_STATIC_INLINE u32 _mali_osk_copy_from_user(void *to, void *from, u32 n) +{ + return (u32)copy_from_user(to, from, (unsigned long)n); +} + +MALI_STATIC_INLINE mali_bool _mali_osk_in_atomic(void) +{ + return in_atomic(); +} + +#define _mali_osk_put_user(x, ptr) put_user(x, ptr) + +#endif /* __MALI_OSK_SPECIFIC_H__ */ diff --git a/drivers/gpu/arm/mali/linux/mali_osk_time.c b/drivers/gpu/arm/mali/linux/mali_osk_time.c new file mode 100644 index 00000000000000..2f1160c006b8fd --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_time.c @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010, 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_time.c + * Implementation of the OS abstraction layer for the kernel device driver + */ + +#include "mali_osk.h" +#include +#include +#include + +int _mali_osk_time_after( u32 ticka, u32 tickb ) +{ + return time_after((unsigned long)ticka, (unsigned long)tickb); +} + +u32 _mali_osk_time_mstoticks( u32 ms ) +{ + return msecs_to_jiffies(ms); +} + +u32 _mali_osk_time_tickstoms( u32 ticks ) +{ + return jiffies_to_msecs(ticks); +} + +u32 _mali_osk_time_tickcount( void ) +{ + return jiffies; +} + +void _mali_osk_time_ubusydelay( u32 usecs ) +{ + udelay(usecs); +} + +u64 _mali_osk_time_get_ns( void ) +{ + struct timespec tsval; + getnstimeofday(&tsval); + return (u64)timespec_to_ns(&tsval); +} diff --git a/drivers/gpu/arm/mali/linux/mali_osk_timers.c b/drivers/gpu/arm/mali/linux/mali_osk_timers.c new file mode 100644 index 00000000000000..3f179be63caf85 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_timers.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_timers.c + * Implementation of the OS abstraction layer for the kernel device driver + */ + +#include +#include +#include "mali_osk.h" +#include "mali_kernel_common.h" + +struct _mali_osk_timer_t_struct { + struct timer_list timer; +}; + +typedef void (*timer_timeout_function_t)(unsigned long); + +_mali_osk_timer_t *_mali_osk_timer_init(void) +{ + _mali_osk_timer_t *t = (_mali_osk_timer_t*)kmalloc(sizeof(_mali_osk_timer_t), GFP_KERNEL); + if (NULL != t) init_timer(&t->timer); + return t; +} + +void _mali_osk_timer_add( _mali_osk_timer_t *tim, u32 ticks_to_expire ) +{ + MALI_DEBUG_ASSERT_POINTER(tim); + tim->timer.expires = jiffies + ticks_to_expire; + add_timer(&(tim->timer)); +} + +void _mali_osk_timer_mod( _mali_osk_timer_t *tim, u32 ticks_to_expire) +{ + MALI_DEBUG_ASSERT_POINTER(tim); + mod_timer(&(tim->timer), jiffies + ticks_to_expire); +} + +void _mali_osk_timer_del( _mali_osk_timer_t *tim ) +{ + MALI_DEBUG_ASSERT_POINTER(tim); + del_timer_sync(&(tim->timer)); +} + +void _mali_osk_timer_del_async( _mali_osk_timer_t *tim ) +{ + MALI_DEBUG_ASSERT_POINTER(tim); + del_timer(&(tim->timer)); +} + +mali_bool _mali_osk_timer_pending( _mali_osk_timer_t *tim ) +{ + MALI_DEBUG_ASSERT_POINTER(tim); + return 1 == timer_pending(&(tim->timer)); +} + +void _mali_osk_timer_setcallback( _mali_osk_timer_t *tim, _mali_osk_timer_callback_t callback, void *data ) +{ + MALI_DEBUG_ASSERT_POINTER(tim); + tim->timer.data = (unsigned long)data; + tim->timer.function = (timer_timeout_function_t)callback; +} + +void _mali_osk_timer_term( _mali_osk_timer_t *tim ) +{ + MALI_DEBUG_ASSERT_POINTER(tim); + kfree(tim); +} diff --git a/drivers/gpu/arm/mali/linux/mali_osk_wait_queue.c b/drivers/gpu/arm/mali/linux/mali_osk_wait_queue.c new file mode 100644 index 00000000000000..ab803b1d6cc19e --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_wait_queue.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_wait_queue.c + * Implemenation of the OS abstraction layer for the kernel device driver + */ + +#include +#include +#include + +#include "mali_osk.h" +#include "mali_kernel_common.h" + +struct _mali_osk_wait_queue_t_struct { + wait_queue_head_t wait_queue; +}; + +_mali_osk_wait_queue_t* _mali_osk_wait_queue_init( void ) +{ + _mali_osk_wait_queue_t* ret = NULL; + + ret = kmalloc(sizeof(_mali_osk_wait_queue_t), GFP_KERNEL); + + if (NULL == ret) { + return ret; + } + + init_waitqueue_head(&ret->wait_queue); + MALI_DEBUG_ASSERT(!waitqueue_active(&ret->wait_queue)); + + return ret; +} + +void _mali_osk_wait_queue_wait_event( _mali_osk_wait_queue_t *queue, mali_bool (*condition)(void *), void *data ) +{ + MALI_DEBUG_ASSERT_POINTER( queue ); + MALI_DEBUG_PRINT(6, ("Adding to wait queue %p\n", queue)); + wait_event(queue->wait_queue, condition(data)); +} + +void _mali_osk_wait_queue_wait_event_timeout( _mali_osk_wait_queue_t *queue, mali_bool (*condition)(void *), void *data, u32 timeout ) +{ + MALI_DEBUG_ASSERT_POINTER( queue ); + MALI_DEBUG_PRINT(6, ("Adding to wait queue %p\n", queue)); + wait_event_timeout(queue->wait_queue, condition(data), _mali_osk_time_mstoticks(timeout)); +} + +void _mali_osk_wait_queue_wake_up( _mali_osk_wait_queue_t *queue ) +{ + MALI_DEBUG_ASSERT_POINTER( queue ); + + /* if queue is empty, don't attempt to wake up its elements */ + if (!waitqueue_active(&queue->wait_queue)) return; + + MALI_DEBUG_PRINT(6, ("Waking up elements in wait queue %p ....\n", queue)); + + wake_up_all(&queue->wait_queue); + + MALI_DEBUG_PRINT(6, ("... elements in wait queue %p woken up\n", queue)); +} + +void _mali_osk_wait_queue_term( _mali_osk_wait_queue_t *queue ) +{ + /* Parameter validation */ + MALI_DEBUG_ASSERT_POINTER( queue ); + + /* Linux requires no explicit termination of wait queues */ + kfree(queue); +} diff --git a/drivers/gpu/arm/mali/linux/mali_osk_wq.c b/drivers/gpu/arm/mali/linux/mali_osk_wq.c new file mode 100644 index 00000000000000..d07977ae42a5ef --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_wq.c @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_wq.c + * Implementation of the OS abstraction layer for the kernel device driver + */ + +#include /* For memory allocation */ +#include +#include +#include + +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_kernel_license.h" +#include "mali_kernel_linux.h" + +typedef struct _mali_osk_wq_work_s { + _mali_osk_wq_work_handler_t handler; + void *data; + mali_bool high_pri; + struct work_struct work_handle; +} mali_osk_wq_work_object_t; + +typedef struct _mali_osk_wq_delayed_work_s { + _mali_osk_wq_work_handler_t handler; + void *data; + struct delayed_work work; +} mali_osk_wq_delayed_work_object_t; + +#if MALI_LICENSE_IS_GPL +struct workqueue_struct *mali_wq_normal = NULL; +struct workqueue_struct *mali_wq_high = NULL; +#endif + +static void _mali_osk_wq_work_func(struct work_struct *work); + +_mali_osk_errcode_t _mali_osk_wq_init(void) +{ +#if MALI_LICENSE_IS_GPL + MALI_DEBUG_ASSERT(NULL == mali_wq_normal); + MALI_DEBUG_ASSERT(NULL == mali_wq_high); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) + mali_wq_normal = alloc_workqueue("mali", WQ_UNBOUND, 0); + mali_wq_high = alloc_workqueue("mali_high_pri", WQ_HIGHPRI, 0); +#else + mali_wq_normal = create_workqueue("mali"); + mali_wq_high = create_workqueue("mali_high_pri"); +#endif + if (NULL == mali_wq_normal || NULL == mali_wq_high) { + MALI_PRINT_ERROR(("Unable to create Mali workqueues\n")); + + if (mali_wq_normal) destroy_workqueue(mali_wq_normal); + if (mali_wq_high) destroy_workqueue(mali_wq_high); + + mali_wq_normal = NULL; + mali_wq_high = NULL; + + return _MALI_OSK_ERR_FAULT; + } +#endif /* MALI_LICENSE_IS_GPL */ + + return _MALI_OSK_ERR_OK; +} + +void _mali_osk_wq_flush(void) +{ +#if MALI_LICENSE_IS_GPL + flush_workqueue(mali_wq_high); + flush_workqueue(mali_wq_normal); +#else + flush_scheduled_work(); +#endif +} + +void _mali_osk_wq_term(void) +{ +#if MALI_LICENSE_IS_GPL + MALI_DEBUG_ASSERT(NULL != mali_wq_normal); + MALI_DEBUG_ASSERT(NULL != mali_wq_high); + + flush_workqueue(mali_wq_normal); + destroy_workqueue(mali_wq_normal); + + flush_workqueue(mali_wq_high); + destroy_workqueue(mali_wq_high); + + mali_wq_normal = NULL; + mali_wq_high = NULL; +#else + flush_scheduled_work(); +#endif +} + +_mali_osk_wq_work_t *_mali_osk_wq_create_work( _mali_osk_wq_work_handler_t handler, void *data ) +{ + mali_osk_wq_work_object_t *work = kmalloc(sizeof(mali_osk_wq_work_object_t), GFP_KERNEL); + + if (NULL == work) return NULL; + + work->handler = handler; + work->data = data; + work->high_pri = MALI_FALSE; + + INIT_WORK( &work->work_handle, _mali_osk_wq_work_func); + + return work; +} + +_mali_osk_wq_work_t *_mali_osk_wq_create_work_high_pri( _mali_osk_wq_work_handler_t handler, void *data ) +{ + mali_osk_wq_work_object_t *work = kmalloc(sizeof(mali_osk_wq_work_object_t), GFP_KERNEL); + + if (NULL == work) return NULL; + + work->handler = handler; + work->data = data; + work->high_pri = MALI_TRUE; + + INIT_WORK( &work->work_handle, _mali_osk_wq_work_func ); + + return work; +} + +void _mali_osk_wq_delete_work( _mali_osk_wq_work_t *work ) +{ + mali_osk_wq_work_object_t *work_object = (mali_osk_wq_work_object_t *)work; + _mali_osk_wq_flush(); + kfree(work_object); +} + +void _mali_osk_wq_delete_work_nonflush( _mali_osk_wq_work_t *work ) +{ + mali_osk_wq_work_object_t *work_object = (mali_osk_wq_work_object_t *)work; + kfree(work_object); +} + +void _mali_osk_wq_schedule_work( _mali_osk_wq_work_t *work ) +{ + mali_osk_wq_work_object_t *work_object = (mali_osk_wq_work_object_t *)work; +#if MALI_LICENSE_IS_GPL + queue_work(mali_wq_normal, &work_object->work_handle); +#else + schedule_work(&work_object->work_handle); +#endif +} + +void _mali_osk_wq_schedule_work_high_pri( _mali_osk_wq_work_t *work ) +{ + mali_osk_wq_work_object_t *work_object = (mali_osk_wq_work_object_t *)work; +#if MALI_LICENSE_IS_GPL + queue_work(mali_wq_high, &work_object->work_handle); +#else + schedule_work(&work_object->work_handle); +#endif +} + +static void _mali_osk_wq_work_func( struct work_struct *work ) +{ + mali_osk_wq_work_object_t *work_object; + + work_object = _MALI_OSK_CONTAINER_OF(work, mali_osk_wq_work_object_t, work_handle); + + /* We want higher priority than the Dynamic Priority, setting it to the lowest of the RT priorities */ + if (MALI_TRUE == work_object->high_pri) { + set_user_nice(current, -19); + } + + work_object->handler(work_object->data); +} + +static void _mali_osk_wq_delayed_work_func( struct work_struct *work ) +{ + mali_osk_wq_delayed_work_object_t *work_object; + + work_object = _MALI_OSK_CONTAINER_OF(work, mali_osk_wq_delayed_work_object_t, work.work); + work_object->handler(work_object->data); +} + +mali_osk_wq_delayed_work_object_t *_mali_osk_wq_delayed_create_work( _mali_osk_wq_work_handler_t handler, void *data) +{ + mali_osk_wq_delayed_work_object_t *work = kmalloc(sizeof(mali_osk_wq_delayed_work_object_t), GFP_KERNEL); + + if (NULL == work) return NULL; + + work->handler = handler; + work->data = data; + + INIT_DELAYED_WORK(&work->work, _mali_osk_wq_delayed_work_func); + + return work; +} + +void _mali_osk_wq_delayed_delete_work_nonflush( _mali_osk_wq_delayed_work_t *work ) +{ + mali_osk_wq_delayed_work_object_t *work_object = (mali_osk_wq_delayed_work_object_t *)work; + kfree(work_object); +} + +void _mali_osk_wq_delayed_cancel_work_async( _mali_osk_wq_delayed_work_t *work ) +{ + mali_osk_wq_delayed_work_object_t *work_object = (mali_osk_wq_delayed_work_object_t *)work; + cancel_delayed_work(&work_object->work); +} + +void _mali_osk_wq_delayed_cancel_work_sync( _mali_osk_wq_delayed_work_t *work ) +{ + mali_osk_wq_delayed_work_object_t *work_object = (mali_osk_wq_delayed_work_object_t *)work; + cancel_delayed_work_sync(&work_object->work); +} + +void _mali_osk_wq_delayed_schedule_work( _mali_osk_wq_delayed_work_t *work, u32 delay ) +{ + mali_osk_wq_delayed_work_object_t *work_object = (mali_osk_wq_delayed_work_object_t *)work; + +#if MALI_LICENSE_IS_GPL + queue_delayed_work(mali_wq_normal, &work_object->work, delay); +#else + schedule_delayed_work(&work_object->work, delay); +#endif + +} diff --git a/drivers/gpu/arm/mali/linux/mali_pmu_power_up_down.c b/drivers/gpu/arm/mali/linux/mali_pmu_power_up_down.c new file mode 100644 index 00000000000000..0c313a311b0b25 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_pmu_power_up_down.c @@ -0,0 +1,71 @@ +/** + * Copyright (C) 2010, 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_pmu_power_up_down.c + */ + +#include +#include +#include +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_pmu.h" +#include "mali_pp_scheduler.h" +#include "linux/mali/mali_utgard.h" + +/* Mali PMU power up/down APIs */ + +int mali_pmu_powerup(void) +{ + struct mali_pmu_core *pmu = mali_pmu_get_global_pmu_core(); + + MALI_DEBUG_PRINT(5, ("Mali PMU: Power up\n")); + + MALI_DEBUG_ASSERT_POINTER(pmu); + if (NULL == pmu) { + return -ENXIO; + } + + if (_MALI_OSK_ERR_OK != mali_pmu_power_up_all(pmu)) { + return -EFAULT; + } + + return 0; +} + +EXPORT_SYMBOL(mali_pmu_powerup); + +int mali_pmu_powerdown(void) +{ + struct mali_pmu_core *pmu = mali_pmu_get_global_pmu_core(); + + MALI_DEBUG_PRINT(5, ("Mali PMU: Power down\n")); + + MALI_DEBUG_ASSERT_POINTER(pmu); + if (NULL == pmu) { + return -ENXIO; + } + + if (_MALI_OSK_ERR_OK != mali_pmu_power_down_all(pmu)) { + return -EFAULT; + } + + return 0; +} + +EXPORT_SYMBOL(mali_pmu_powerdown); + +int mali_perf_set_num_pp_cores(unsigned int num_cores) +{ + return mali_pp_scheduler_set_perf_level(num_cores, MALI_FALSE); +} + +EXPORT_SYMBOL(mali_perf_set_num_pp_cores); diff --git a/drivers/gpu/arm/mali/linux/mali_profiling_events.h b/drivers/gpu/arm/mali/linux/mali_profiling_events.h new file mode 100644 index 00000000000000..2639a404d47578 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_profiling_events.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_PROFILING_EVENTS_H__ +#define __MALI_PROFILING_EVENTS_H__ + +/* Simple wrapper in order to find the OS specific location of this file */ +#include + +#endif /* __MALI_PROFILING_EVENTS_H__ */ diff --git a/drivers/gpu/arm/mali/linux/mali_profiling_gator_api.h b/drivers/gpu/arm/mali/linux/mali_profiling_gator_api.h new file mode 100644 index 00000000000000..e3d836fedef765 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_profiling_gator_api.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_PROFILING_GATOR_API_H__ +#define __MALI_PROFILING_GATOR_API_H__ + +/* Simple wrapper in order to find the OS specific location of this file */ +#include + +#endif /* __MALI_PROFILING_GATOR_API_H__ */ diff --git a/drivers/gpu/arm/mali/linux/mali_profiling_internal.c b/drivers/gpu/arm/mali/linux/mali_profiling_internal.c new file mode 100644 index 00000000000000..c05028bb9579bf --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_profiling_internal.c @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_osk_mali.h" +#include "mali_ukk.h" +#include "mali_timestamp.h" +#include "mali_osk_profiling.h" +#include "mali_user_settings_db.h" +#include "mali_profiling_internal.h" + +typedef struct mali_profiling_entry { + u64 timestamp; + u32 event_id; + u32 data[5]; +} mali_profiling_entry; + +typedef enum mali_profiling_state { + MALI_PROFILING_STATE_UNINITIALIZED, + MALI_PROFILING_STATE_IDLE, + MALI_PROFILING_STATE_RUNNING, + MALI_PROFILING_STATE_RETURN, +} mali_profiling_state; + +static _mali_osk_mutex_t *lock = NULL; +static mali_profiling_state prof_state = MALI_PROFILING_STATE_UNINITIALIZED; +static mali_profiling_entry* profile_entries = NULL; +static _mali_osk_atomic_t profile_insert_index; +static u32 profile_mask = 0; + +static inline void add_event(u32 event_id, u32 data0, u32 data1, u32 data2, u32 data3, u32 data4); + +void probe_mali_timeline_event(void *data, TP_PROTO(unsigned int event_id, unsigned int d0, unsigned int d1, unsigned + int d2, unsigned int d3, unsigned int d4)) +{ + add_event(event_id, d0, d1, d2, d3, d4); +} + +_mali_osk_errcode_t _mali_internal_profiling_init(mali_bool auto_start) +{ + profile_entries = NULL; + profile_mask = 0; + _mali_osk_atomic_init(&profile_insert_index, 0); + + lock = _mali_osk_mutex_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_PROFILING); + if (NULL == lock) { + return _MALI_OSK_ERR_FAULT; + } + + prof_state = MALI_PROFILING_STATE_IDLE; + + if (MALI_TRUE == auto_start) { + u32 limit = MALI_PROFILING_MAX_BUFFER_ENTRIES; /* Use maximum buffer size */ + + mali_set_user_setting(_MALI_UK_USER_SETTING_SW_EVENTS_ENABLE, MALI_TRUE); + if (_MALI_OSK_ERR_OK != _mali_internal_profiling_start(&limit)) { + return _MALI_OSK_ERR_FAULT; + } + } + + return _MALI_OSK_ERR_OK; +} + +void _mali_internal_profiling_term(void) +{ + u32 count; + + /* Ensure profiling is stopped */ + _mali_internal_profiling_stop(&count); + + prof_state = MALI_PROFILING_STATE_UNINITIALIZED; + + if (NULL != profile_entries) { + _mali_osk_vfree(profile_entries); + profile_entries = NULL; + } + + if (NULL != lock) { + _mali_osk_mutex_term(lock); + lock = NULL; + } +} + +_mali_osk_errcode_t _mali_internal_profiling_start(u32 * limit) +{ + _mali_osk_errcode_t ret; + mali_profiling_entry *new_profile_entries; + + _mali_osk_mutex_wait(lock); + + if (MALI_PROFILING_STATE_RUNNING == prof_state) { + _mali_osk_mutex_signal(lock); + return _MALI_OSK_ERR_BUSY; + } + + new_profile_entries = _mali_osk_valloc(*limit * sizeof(mali_profiling_entry)); + + if (NULL == new_profile_entries) { + _mali_osk_vfree(new_profile_entries); + return _MALI_OSK_ERR_NOMEM; + } + + if (MALI_PROFILING_MAX_BUFFER_ENTRIES < *limit) { + *limit = MALI_PROFILING_MAX_BUFFER_ENTRIES; + } + + profile_mask = 1; + while (profile_mask <= *limit) { + profile_mask <<= 1; + } + profile_mask >>= 1; + + *limit = profile_mask; + + profile_mask--; /* turns the power of two into a mask of one less */ + + if (MALI_PROFILING_STATE_IDLE != prof_state) { + _mali_osk_mutex_signal(lock); + _mali_osk_vfree(new_profile_entries); + return _MALI_OSK_ERR_INVALID_ARGS; /* invalid to call this function in this state */ + } + + profile_entries = new_profile_entries; + + ret = _mali_timestamp_reset(); + + if (_MALI_OSK_ERR_OK == ret) { + prof_state = MALI_PROFILING_STATE_RUNNING; + } else { + _mali_osk_vfree(profile_entries); + profile_entries = NULL; + } + + register_trace_mali_timeline_event(probe_mali_timeline_event, NULL); + + _mali_osk_mutex_signal(lock); + return ret; +} + +static inline void add_event(u32 event_id, u32 data0, u32 data1, u32 data2, u32 data3, u32 data4) +{ + u32 cur_index = (_mali_osk_atomic_inc_return(&profile_insert_index) - 1) & profile_mask; + + profile_entries[cur_index].timestamp = _mali_timestamp_get(); + profile_entries[cur_index].event_id = event_id; + profile_entries[cur_index].data[0] = data0; + profile_entries[cur_index].data[1] = data1; + profile_entries[cur_index].data[2] = data2; + profile_entries[cur_index].data[3] = data3; + profile_entries[cur_index].data[4] = data4; + + /* If event is "leave API function", add current memory usage to the event + * as data point 4. This is used in timeline profiling to indicate how + * much memory was used when leaving a function. */ + if (event_id == (MALI_PROFILING_EVENT_TYPE_SINGLE|MALI_PROFILING_EVENT_CHANNEL_SOFTWARE|MALI_PROFILING_EVENT_REASON_SINGLE_SW_LEAVE_API_FUNC)) { + profile_entries[cur_index].data[4] = _mali_ukk_report_memory_usage(); + } +} + +_mali_osk_errcode_t _mali_internal_profiling_stop(u32 * count) +{ + _mali_osk_mutex_wait(lock); + + if (MALI_PROFILING_STATE_RUNNING != prof_state) { + _mali_osk_mutex_signal(lock); + return _MALI_OSK_ERR_INVALID_ARGS; /* invalid to call this function in this state */ + } + + /* go into return state (user to retreive events), no more events will be added after this */ + prof_state = MALI_PROFILING_STATE_RETURN; + + unregister_trace_mali_timeline_event(probe_mali_timeline_event, NULL); + + _mali_osk_mutex_signal(lock); + + tracepoint_synchronize_unregister(); + + *count = _mali_osk_atomic_read(&profile_insert_index); + if (*count > profile_mask) *count = profile_mask; + + return _MALI_OSK_ERR_OK; +} + +u32 _mali_internal_profiling_get_count(void) +{ + u32 retval = 0; + + _mali_osk_mutex_wait(lock); + if (MALI_PROFILING_STATE_RETURN == prof_state) { + retval = _mali_osk_atomic_read(&profile_insert_index); + if (retval > profile_mask) retval = profile_mask; + } + _mali_osk_mutex_signal(lock); + + return retval; +} + +_mali_osk_errcode_t _mali_internal_profiling_get_event(u32 index, u64* timestamp, u32* event_id, u32 data[5]) +{ + u32 raw_index = _mali_osk_atomic_read(&profile_insert_index); + + _mali_osk_mutex_wait(lock); + + if (index < profile_mask) { + if ((raw_index & ~profile_mask) != 0) { + index += raw_index; + index &= profile_mask; + } + + if (prof_state != MALI_PROFILING_STATE_RETURN) { + _mali_osk_mutex_signal(lock); + return _MALI_OSK_ERR_INVALID_ARGS; /* invalid to call this function in this state */ + } + + if(index >= raw_index) { + _mali_osk_mutex_signal(lock); + return _MALI_OSK_ERR_FAULT; + } + + *timestamp = profile_entries[index].timestamp; + *event_id = profile_entries[index].event_id; + data[0] = profile_entries[index].data[0]; + data[1] = profile_entries[index].data[1]; + data[2] = profile_entries[index].data[2]; + data[3] = profile_entries[index].data[3]; + data[4] = profile_entries[index].data[4]; + } else { + _mali_osk_mutex_signal(lock); + return _MALI_OSK_ERR_FAULT; + } + + _mali_osk_mutex_signal(lock); + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t _mali_internal_profiling_clear(void) +{ + _mali_osk_mutex_wait(lock); + + if (MALI_PROFILING_STATE_RETURN != prof_state) { + _mali_osk_mutex_signal(lock); + return _MALI_OSK_ERR_INVALID_ARGS; /* invalid to call this function in this state */ + } + + prof_state = MALI_PROFILING_STATE_IDLE; + profile_mask = 0; + _mali_osk_atomic_init(&profile_insert_index, 0); + + if (NULL != profile_entries) { + _mali_osk_vfree(profile_entries); + profile_entries = NULL; + } + + _mali_osk_mutex_signal(lock); + return _MALI_OSK_ERR_OK; +} + +mali_bool _mali_internal_profiling_is_recording(void) +{ + return prof_state == MALI_PROFILING_STATE_RUNNING ? MALI_TRUE : MALI_FALSE; +} + +mali_bool _mali_internal_profiling_have_recording(void) +{ + return prof_state == MALI_PROFILING_STATE_RETURN ? MALI_TRUE : MALI_FALSE; +} diff --git a/drivers/gpu/arm/mali/linux/mali_profiling_internal.h b/drivers/gpu/arm/mali/linux/mali_profiling_internal.h new file mode 100644 index 00000000000000..3c86e077bcd948 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_profiling_internal.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_PROFILING_INTERNAL_H__ +#define __MALI_PROFILING_INTERNAL_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "mali_osk.h" + +int _mali_internal_profiling_init(mali_bool auto_start); +void _mali_internal_profiling_term(void); + +mali_bool _mali_internal_profiling_is_recording(void); +mali_bool _mali_internal_profiling_have_recording(void); +_mali_osk_errcode_t _mali_internal_profiling_clear(void); +_mali_osk_errcode_t _mali_internal_profiling_get_event(u32 index, u64* timestamp, u32* event_id, u32 data[5]); +u32 _mali_internal_profiling_get_count(void); +int _mali_internal_profiling_stop(u32 * count); +int _mali_internal_profiling_start(u32 * limit); + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_PROFILING_INTERNAL_H__ */ diff --git a/drivers/gpu/arm/mali/linux/mali_sync.c b/drivers/gpu/arm/mali/linux/mali_sync.c new file mode 100644 index 00000000000000..d85d5bbe276586 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_sync.c @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_sync.h" + +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_timeline.h" + +#include +#include +#include + +struct mali_sync_pt { + struct sync_pt sync_pt; + struct mali_sync_flag *flag; +}; + +/** + * The sync flag is used to connect sync fences to the Mali Timeline system. Sync fences can be + * created from a sync flag, and when the flag is signaled, the sync fences will also be signaled. + */ +struct mali_sync_flag { + struct sync_timeline *sync_tl; /**< Sync timeline this flag is connected to. */ + u32 point; /**< Point on timeline. */ + int status; /**< 0 if unsignaled, 1 if signaled without error or negative if signaled with error. */ + struct kref refcount; /**< Reference count. */ +}; + +MALI_STATIC_INLINE struct mali_sync_pt *to_mali_sync_pt(struct sync_pt *pt) +{ + return container_of(pt, struct mali_sync_pt, sync_pt); +} + +static struct sync_pt *timeline_dup(struct sync_pt *pt) +{ + struct mali_sync_pt *mpt, *new_mpt; + struct sync_pt *new_pt; + + MALI_DEBUG_ASSERT_POINTER(pt); + mpt = to_mali_sync_pt(pt); + + new_pt = sync_pt_create(pt->parent, sizeof(struct mali_sync_pt)); + if (NULL == new_pt) return NULL; + + new_mpt = to_mali_sync_pt(new_pt); + + mali_sync_flag_get(mpt->flag); + new_mpt->flag = mpt->flag; + + return new_pt; +} + +static int timeline_has_signaled(struct sync_pt *pt) +{ + struct mali_sync_pt *mpt; + + MALI_DEBUG_ASSERT_POINTER(pt); + mpt = to_mali_sync_pt(pt); + + MALI_DEBUG_ASSERT_POINTER(mpt->flag); + + return mpt->flag->status; +} + +static int timeline_compare(struct sync_pt *pta, struct sync_pt *ptb) +{ + struct mali_sync_pt *mpta; + struct mali_sync_pt *mptb; + u32 a, b; + + MALI_DEBUG_ASSERT_POINTER(pta); + MALI_DEBUG_ASSERT_POINTER(ptb); + mpta = to_mali_sync_pt(pta); + mptb = to_mali_sync_pt(ptb); + + MALI_DEBUG_ASSERT_POINTER(mpta->flag); + MALI_DEBUG_ASSERT_POINTER(mptb->flag); + + a = mpta->flag->point; + b = mpta->flag->point; + + if (a == b) return 0; + + return ((b - a) < (a - b) ? -1 : 1); +} + +static void timeline_free_pt(struct sync_pt *pt) +{ + struct mali_sync_pt *mpt; + + MALI_DEBUG_ASSERT_POINTER(pt); + mpt = to_mali_sync_pt(pt); + + mali_sync_flag_put(mpt->flag); +} + +static void timeline_release(struct sync_timeline *sync_timeline) +{ + module_put(THIS_MODULE); +} + +static void timeline_print_pt(struct seq_file *s, struct sync_pt *sync_pt) +{ + struct mali_sync_pt *mpt; + + MALI_DEBUG_ASSERT_POINTER(s); + MALI_DEBUG_ASSERT_POINTER(sync_pt); + + mpt = to_mali_sync_pt(sync_pt); + MALI_DEBUG_ASSERT_POINTER(mpt->flag); + + seq_printf(s, "%u", mpt->flag->point); +} + +static struct sync_timeline_ops mali_timeline_ops = { + .driver_name = "Mali", + .dup = timeline_dup, + .has_signaled = timeline_has_signaled, + .compare = timeline_compare, + .free_pt = timeline_free_pt, + .release_obj = timeline_release, + .print_pt = timeline_print_pt, +}; + +struct sync_timeline *mali_sync_timeline_create(const char *name) +{ + struct sync_timeline *sync_tl; + + sync_tl = sync_timeline_create(&mali_timeline_ops, sizeof(struct sync_timeline), name); + if (NULL == sync_tl) return NULL; + + /* Grab a reference on the module to ensure the callbacks are present + * as long some timeline exists. The reference is released when the + * timeline is freed. + * Since this function is called from a ioctl on an open file we know + * we already have a reference, so using __module_get is safe. */ + __module_get(THIS_MODULE); + + return sync_tl; +} + +mali_bool mali_sync_timeline_is_ours(struct sync_timeline *sync_tl) +{ + MALI_DEBUG_ASSERT_POINTER(sync_tl); + return (sync_tl->ops == &mali_timeline_ops) ? MALI_TRUE : MALI_FALSE; +} + +s32 mali_sync_fence_fd_alloc(struct sync_fence *sync_fence) +{ + s32 fd = -1; + + fd = get_unused_fd(); + if (fd < 0) { + sync_fence_put(sync_fence); + return -1; + } + sync_fence_install(sync_fence, fd); + + return fd; +} + +struct sync_fence *mali_sync_fence_merge(struct sync_fence *sync_fence1, struct sync_fence *sync_fence2) +{ + struct sync_fence *sync_fence; + + MALI_DEBUG_ASSERT_POINTER(sync_fence1); + MALI_DEBUG_ASSERT_POINTER(sync_fence1); + + sync_fence = sync_fence_merge("mali_merge_fence", sync_fence1, sync_fence2); + sync_fence_put(sync_fence1); + sync_fence_put(sync_fence2); + + return sync_fence; +} + +struct sync_fence *mali_sync_timeline_create_signaled_fence(struct sync_timeline *sync_tl) +{ + struct mali_sync_flag *flag; + struct sync_fence *sync_fence; + + MALI_DEBUG_ASSERT_POINTER(sync_tl); + + flag = mali_sync_flag_create(sync_tl, 0); + if (NULL == flag) return NULL; + + sync_fence = mali_sync_flag_create_fence(flag); + + mali_sync_flag_signal(flag, 0); + mali_sync_flag_put(flag); + + return sync_fence; +} + +struct mali_sync_flag *mali_sync_flag_create(struct sync_timeline *sync_tl, mali_timeline_point point) +{ + struct mali_sync_flag *flag; + + if (NULL == sync_tl) return NULL; + + flag = _mali_osk_calloc(1, sizeof(*flag)); + if (NULL == flag) return NULL; + + flag->sync_tl = sync_tl; + flag->point = point; + + flag->status = 0; + kref_init(&flag->refcount); + + return flag; +} + +void mali_sync_flag_get(struct mali_sync_flag *flag) +{ + MALI_DEBUG_ASSERT_POINTER(flag); + kref_get(&flag->refcount); +} + +/** + * Free sync flag. + * + * @param ref kref object embedded in sync flag that should be freed. + */ +static void mali_sync_flag_free(struct kref *ref) +{ + struct mali_sync_flag *flag; + + MALI_DEBUG_ASSERT_POINTER(ref); + flag = container_of(ref, struct mali_sync_flag, refcount); + + _mali_osk_free(flag); +} + +void mali_sync_flag_put(struct mali_sync_flag *flag) +{ + MALI_DEBUG_ASSERT_POINTER(flag); + kref_put(&flag->refcount, mali_sync_flag_free); +} + +void mali_sync_flag_signal(struct mali_sync_flag *flag, int error) +{ + MALI_DEBUG_ASSERT_POINTER(flag); + + MALI_DEBUG_ASSERT(0 == flag->status); + flag->status = (0 > error) ? error : 1; + + _mali_osk_write_mem_barrier(); + + sync_timeline_signal(flag->sync_tl); +} + +/** + * Create a sync point attached to given sync flag. + * + * @note Sync points must be triggered in *exactly* the same order as they are created. + * + * @param flag Sync flag. + * @return New sync point if successful, NULL if not. + */ +static struct sync_pt *mali_sync_flag_create_pt(struct mali_sync_flag *flag) +{ + struct sync_pt *pt; + struct mali_sync_pt *mpt; + + MALI_DEBUG_ASSERT_POINTER(flag); + MALI_DEBUG_ASSERT_POINTER(flag->sync_tl); + + pt = sync_pt_create(flag->sync_tl, sizeof(struct mali_sync_pt)); + if (NULL == pt) return NULL; + + mali_sync_flag_get(flag); + + mpt = to_mali_sync_pt(pt); + mpt->flag = flag; + + return pt; +} + +struct sync_fence *mali_sync_flag_create_fence(struct mali_sync_flag *flag) +{ + struct sync_pt *sync_pt; + struct sync_fence *sync_fence; + + MALI_DEBUG_ASSERT_POINTER(flag); + MALI_DEBUG_ASSERT_POINTER(flag->sync_tl); + + sync_pt = mali_sync_flag_create_pt(flag); + if (NULL == sync_pt) return NULL; + + sync_fence = sync_fence_create("mali_flag_fence", sync_pt); + if (NULL == sync_fence) { + sync_pt_free(sync_pt); + return NULL; + } + + return sync_fence; +} diff --git a/drivers/gpu/arm/mali/linux/mali_sync.h b/drivers/gpu/arm/mali/linux/mali_sync.h new file mode 100644 index 00000000000000..6b131a6b9582f8 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_sync.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_sync.h + * + * Mali interface for Linux sync objects. + */ + +#ifndef _MALI_SYNC_H_ +#define _MALI_SYNC_H_ + +#if defined(CONFIG_SYNC) + +#include +#include + +#include "mali_osk.h" + +struct mali_sync_flag; + +/** + * Create a sync timeline. + * + * @param name Name of the sync timeline. + * @return The new sync timeline if successful, NULL if not. + */ +struct sync_timeline *mali_sync_timeline_create(const char *name); + +/** + * Check if sync timeline belongs to Mali. + * + * @param sync_tl Sync timeline to check. + * @return MALI_TRUE if sync timeline belongs to Mali, MALI_FALSE if not. + */ +mali_bool mali_sync_timeline_is_ours(struct sync_timeline *sync_tl); + +/** + * Creates a file descriptor representing the sync fence. Will release sync fence if allocation of + * file descriptor fails. + * + * @param sync_fence Sync fence. + * @return File descriptor representing sync fence if successful, or -1 if not. + */ +s32 mali_sync_fence_fd_alloc(struct sync_fence *sync_fence); + +/** + * Merges two sync fences. Both input sync fences will be released. + * + * @param sync_fence1 First sync fence. + * @param sync_fence2 Second sync fence. + * @return New sync fence that is the result of the merger if successful, or NULL if not. + */ +struct sync_fence *mali_sync_fence_merge(struct sync_fence *sync_fence1, struct sync_fence *sync_fence2); + +/** + * Create a sync fence that is already signaled. + * + * @param tl Sync timeline. + * @return New signaled sync fence if successful, NULL if not. + */ +struct sync_fence *mali_sync_timeline_create_signaled_fence(struct sync_timeline *sync_tl); + +/** + * Create a sync flag. + * + * @param sync_tl Sync timeline. + * @param point Point on Mali timeline. + * @return New sync flag if successful, NULL if not. + */ +struct mali_sync_flag *mali_sync_flag_create(struct sync_timeline *sync_tl, u32 point); + +/** + * Grab sync flag reference. + * + * @param flag Sync flag. + */ +void mali_sync_flag_get(struct mali_sync_flag *flag); + +/** + * Release sync flag reference. If this was the last reference, the sync flag will be freed. + * + * @param flag Sync flag. + */ +void mali_sync_flag_put(struct mali_sync_flag *flag); + +/** + * Signal sync flag. All sync fences created from this flag will be signaled. + * + * @param flag Sync flag to signal. + * @param error Negative error code, or 0 if no error. + */ +void mali_sync_flag_signal(struct mali_sync_flag *flag, int error); + +/** + * Create a sync fence attached to given sync flag. + * + * @param flag Sync flag. + * @return New sync fence if successful, NULL if not. + */ +struct sync_fence *mali_sync_flag_create_fence(struct mali_sync_flag *flag); + +#endif /* defined(CONFIG_SYNC) */ + +#endif /* _MALI_SYNC_H_ */ diff --git a/drivers/gpu/arm/mali/linux/mali_uk_types.h b/drivers/gpu/arm/mali/linux/mali_uk_types.h new file mode 100644 index 00000000000000..fbe902a02d4329 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_uk_types.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_UK_TYPES_H__ +#define __MALI_UK_TYPES_H__ + +/* Simple wrapper in order to find the OS specific location of this file */ +#include + +#endif /* __MALI_UK_TYPES_H__ */ diff --git a/drivers/gpu/arm/mali/linux/mali_ukk_core.c b/drivers/gpu/arm/mali/linux/mali_ukk_core.c new file mode 100644 index 00000000000000..c0372d71986591 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_ukk_core.c @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include /* file system operations */ +#include /* memort allocation functions */ +#include /* user space access */ + +#include "mali_ukk.h" +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_session.h" +#include "mali_ukk_wrappers.h" + +int get_api_version_wrapper(struct mali_session_data *session_data, _mali_uk_get_api_version_s __user *uargs) +{ + _mali_uk_get_api_version_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + if (0 != get_user(kargs.version, &uargs->version)) return -EFAULT; + + kargs.ctx = session_data; + err = _mali_ukk_get_api_version(&kargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + if (0 != put_user(kargs.version, &uargs->version)) return -EFAULT; + if (0 != put_user(kargs.compatible, &uargs->compatible)) return -EFAULT; + + return 0; +} + +int wait_for_notification_wrapper(struct mali_session_data *session_data, _mali_uk_wait_for_notification_s __user *uargs) +{ + _mali_uk_wait_for_notification_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + kargs.ctx = session_data; + err = _mali_ukk_wait_for_notification(&kargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + if(_MALI_NOTIFICATION_CORE_SHUTDOWN_IN_PROGRESS != kargs.type) { + kargs.ctx = NULL; /* prevent kernel address to be returned to user space */ + if (0 != copy_to_user(uargs, &kargs, sizeof(_mali_uk_wait_for_notification_s))) return -EFAULT; + } else { + if (0 != put_user(kargs.type, &uargs->type)) return -EFAULT; + } + + return 0; +} + +int post_notification_wrapper(struct mali_session_data *session_data, _mali_uk_post_notification_s __user *uargs) +{ + _mali_uk_post_notification_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + kargs.ctx = session_data; + + if (0 != get_user(kargs.type, &uargs->type)) { + return -EFAULT; + } + + err = _mali_ukk_post_notification(&kargs); + if (_MALI_OSK_ERR_OK != err) { + return map_errcode(err); + } + + return 0; +} + +int get_user_settings_wrapper(struct mali_session_data *session_data, _mali_uk_get_user_settings_s __user *uargs) +{ + _mali_uk_get_user_settings_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + kargs.ctx = session_data; + err = _mali_ukk_get_user_settings(&kargs); + if (_MALI_OSK_ERR_OK != err) { + return map_errcode(err); + } + + kargs.ctx = NULL; /* prevent kernel address to be returned to user space */ + if (0 != copy_to_user(uargs, &kargs, sizeof(_mali_uk_get_user_settings_s))) return -EFAULT; + + return 0; +} + +int request_high_priority_wrapper(struct mali_session_data *session_data, _mali_uk_request_high_priority_s __user *uargs) +{ + _mali_uk_request_high_priority_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + kargs.ctx = session_data; + err = _mali_ukk_request_high_priority(&kargs); + + kargs.ctx = NULL; + + return map_errcode(err); +} diff --git a/drivers/gpu/arm/mali/linux/mali_ukk_gp.c b/drivers/gpu/arm/mali/linux/mali_ukk_gp.c new file mode 100644 index 00000000000000..32b148b1856bce --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_ukk_gp.c @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2010, 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include /* file system operations */ +#include /* user space access */ + +#include "mali_ukk.h" +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_session.h" +#include "mali_ukk_wrappers.h" + +int gp_start_job_wrapper(struct mali_session_data *session_data, _mali_uk_gp_start_job_s __user *uargs) +{ + _mali_osk_errcode_t err; + + /* If the job was started successfully, 0 is returned. If there was an error, but the job + * was started, we return -ENOENT. For anything else returned, the job was not started. */ + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session_data, -EINVAL); + + err = _mali_ukk_gp_start_job(session_data, uargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + return 0; +} + +int gp_get_core_version_wrapper(struct mali_session_data *session_data, _mali_uk_get_gp_core_version_s __user *uargs) +{ + _mali_uk_get_gp_core_version_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session_data, -EINVAL); + + kargs.ctx = session_data; + err = _mali_ukk_get_gp_core_version(&kargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + /* no known transactions to roll-back */ + + if (0 != put_user(kargs.version, &uargs->version)) return -EFAULT; + + return 0; +} + +int gp_suspend_response_wrapper(struct mali_session_data *session_data, _mali_uk_gp_suspend_response_s __user *uargs) +{ + _mali_uk_gp_suspend_response_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session_data, -EINVAL); + + if (0 != copy_from_user(&kargs, uargs, sizeof(_mali_uk_gp_suspend_response_s))) return -EFAULT; + + kargs.ctx = session_data; + err = _mali_ukk_gp_suspend_response(&kargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + if (0 != put_user(kargs.cookie, &uargs->cookie)) return -EFAULT; + + /* no known transactions to roll-back */ + return 0; +} + +int gp_get_number_of_cores_wrapper(struct mali_session_data *session_data, _mali_uk_get_gp_number_of_cores_s __user *uargs) +{ + _mali_uk_get_gp_number_of_cores_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session_data, -EINVAL); + + kargs.ctx = session_data; + err = _mali_ukk_get_gp_number_of_cores(&kargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + /* no known transactions to roll-back */ + + if (0 != put_user(kargs.number_of_cores, &uargs->number_of_cores)) return -EFAULT; + + return 0; +} diff --git a/drivers/gpu/arm/mali/linux/mali_ukk_mem.c b/drivers/gpu/arm/mali/linux/mali_ukk_mem.c new file mode 100644 index 00000000000000..083c909ab09ef1 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_ukk_mem.c @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include /* file system operations */ +#include /* user space access */ + +#include "mali_ukk.h" +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_session.h" +#include "mali_ukk_wrappers.h" + +int mem_write_safe_wrapper(struct mali_session_data *session_data, _mali_uk_mem_write_safe_s __user * uargs) +{ + _mali_uk_mem_write_safe_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session_data, -EINVAL); + + if (0 != copy_from_user(&kargs, uargs, sizeof(_mali_uk_mem_write_safe_s))) { + return -EFAULT; + } + + kargs.ctx = session_data; + + /* Check if we can access the buffers */ + if (!access_ok(VERIFY_WRITE, kargs.dest, kargs.size) + || !access_ok(VERIFY_READ, kargs.src, kargs.size)) { + return -EINVAL; + } + + /* Check if size wraps */ + if ((kargs.size + kargs.dest) <= kargs.dest + || (kargs.size + kargs.src) <= kargs.src) { + return -EINVAL; + } + + err = _mali_ukk_mem_write_safe(&kargs); + if (_MALI_OSK_ERR_OK != err) { + return map_errcode(err); + } + + if (0 != put_user(kargs.size, &uargs->size)) { + return -EFAULT; + } + + return 0; +} + +int mem_map_ext_wrapper(struct mali_session_data *session_data, _mali_uk_map_external_mem_s __user * argument) +{ + _mali_uk_map_external_mem_s uk_args; + _mali_osk_errcode_t err_code; + + /* validate input */ + /* the session_data pointer was validated by caller */ + MALI_CHECK_NON_NULL( argument, -EINVAL); + + /* get call arguments from user space. copy_from_user returns how many bytes which where NOT copied */ + if ( 0 != copy_from_user(&uk_args, (void __user *)argument, sizeof(_mali_uk_map_external_mem_s)) ) { + return -EFAULT; + } + + uk_args.ctx = session_data; + err_code = _mali_ukk_map_external_mem( &uk_args ); + + if (0 != put_user(uk_args.cookie, &argument->cookie)) { + if (_MALI_OSK_ERR_OK == err_code) { + /* Rollback */ + _mali_uk_unmap_external_mem_s uk_args_unmap; + + uk_args_unmap.ctx = session_data; + uk_args_unmap.cookie = uk_args.cookie; + err_code = _mali_ukk_unmap_external_mem( &uk_args_unmap ); + if (_MALI_OSK_ERR_OK != err_code) { + MALI_DEBUG_PRINT(4, ("reverting _mali_ukk_unmap_external_mem, as a result of failing put_user(), failed\n")); + } + } + return -EFAULT; + } + + /* Return the error that _mali_ukk_free_big_block produced */ + return map_errcode(err_code); +} + +int mem_unmap_ext_wrapper(struct mali_session_data *session_data, _mali_uk_unmap_external_mem_s __user * argument) +{ + _mali_uk_unmap_external_mem_s uk_args; + _mali_osk_errcode_t err_code; + + /* validate input */ + /* the session_data pointer was validated by caller */ + MALI_CHECK_NON_NULL( argument, -EINVAL); + + /* get call arguments from user space. copy_from_user returns how many bytes which where NOT copied */ + if ( 0 != copy_from_user(&uk_args, (void __user *)argument, sizeof(_mali_uk_unmap_external_mem_s)) ) { + return -EFAULT; + } + + uk_args.ctx = session_data; + err_code = _mali_ukk_unmap_external_mem( &uk_args ); + + /* Return the error that _mali_ukk_free_big_block produced */ + return map_errcode(err_code); +} + +#if defined(CONFIG_MALI400_UMP) +int mem_release_ump_wrapper(struct mali_session_data *session_data, _mali_uk_release_ump_mem_s __user * argument) +{ + _mali_uk_release_ump_mem_s uk_args; + _mali_osk_errcode_t err_code; + + /* validate input */ + /* the session_data pointer was validated by caller */ + MALI_CHECK_NON_NULL( argument, -EINVAL); + + /* get call arguments from user space. copy_from_user returns how many bytes which where NOT copied */ + if ( 0 != copy_from_user(&uk_args, (void __user *)argument, sizeof(_mali_uk_release_ump_mem_s)) ) { + return -EFAULT; + } + + uk_args.ctx = session_data; + err_code = _mali_ukk_release_ump_mem( &uk_args ); + + /* Return the error that _mali_ukk_free_big_block produced */ + return map_errcode(err_code); +} + +int mem_attach_ump_wrapper(struct mali_session_data *session_data, _mali_uk_attach_ump_mem_s __user * argument) +{ + _mali_uk_attach_ump_mem_s uk_args; + _mali_osk_errcode_t err_code; + + /* validate input */ + /* the session_data pointer was validated by caller */ + MALI_CHECK_NON_NULL( argument, -EINVAL); + + /* get call arguments from user space. copy_from_user returns how many bytes which where NOT copied */ + if ( 0 != copy_from_user(&uk_args, (void __user *)argument, sizeof(_mali_uk_attach_ump_mem_s)) ) { + return -EFAULT; + } + + uk_args.ctx = session_data; + err_code = _mali_ukk_attach_ump_mem( &uk_args ); + + if (0 != put_user(uk_args.cookie, &argument->cookie)) { + if (_MALI_OSK_ERR_OK == err_code) { + /* Rollback */ + _mali_uk_release_ump_mem_s uk_args_unmap; + + uk_args_unmap.ctx = session_data; + uk_args_unmap.cookie = uk_args.cookie; + err_code = _mali_ukk_release_ump_mem( &uk_args_unmap ); + if (_MALI_OSK_ERR_OK != err_code) { + MALI_DEBUG_PRINT(4, ("reverting _mali_ukk_attach_mem, as a result of failing put_user(), failed\n")); + } + } + return -EFAULT; + } + + /* Return the error that _mali_ukk_map_external_ump_mem produced */ + return map_errcode(err_code); +} +#endif /* CONFIG_MALI400_UMP */ + +int mem_query_mmu_page_table_dump_size_wrapper(struct mali_session_data *session_data, _mali_uk_query_mmu_page_table_dump_size_s __user * uargs) +{ + _mali_uk_query_mmu_page_table_dump_size_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session_data, -EINVAL); + + kargs.ctx = session_data; + + err = _mali_ukk_query_mmu_page_table_dump_size(&kargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + if (0 != put_user(kargs.size, &uargs->size)) return -EFAULT; + + return 0; +} + +int mem_dump_mmu_page_table_wrapper(struct mali_session_data *session_data, _mali_uk_dump_mmu_page_table_s __user * uargs) +{ + _mali_uk_dump_mmu_page_table_s kargs; + _mali_osk_errcode_t err; + void *buffer; + int rc = -EFAULT; + + /* validate input */ + MALI_CHECK_NON_NULL(uargs, -EINVAL); + /* the session_data pointer was validated by caller */ + + kargs.buffer = NULL; + + /* get location of user buffer */ + if (0 != get_user(buffer, &uargs->buffer)) goto err_exit; + /* get size of mmu page table info buffer from user space */ + if ( 0 != get_user(kargs.size, &uargs->size) ) goto err_exit; + /* verify we can access the whole of the user buffer */ + if (!access_ok(VERIFY_WRITE, buffer, kargs.size)) goto err_exit; + + /* allocate temporary buffer (kernel side) to store mmu page table info */ + MALI_CHECK(kargs.size > 0, -ENOMEM); + kargs.buffer = _mali_osk_valloc(kargs.size); + if (NULL == kargs.buffer) { + rc = -ENOMEM; + goto err_exit; + } + + kargs.ctx = session_data; + err = _mali_ukk_dump_mmu_page_table(&kargs); + if (_MALI_OSK_ERR_OK != err) { + rc = map_errcode(err); + goto err_exit; + } + + /* copy mmu page table info back to user space and update pointers */ + if (0 != copy_to_user(uargs->buffer, kargs.buffer, kargs.size) ) goto err_exit; + if (0 != put_user((kargs.register_writes - (u32 *)kargs.buffer) + (u32 *)uargs->buffer, &uargs->register_writes)) goto err_exit; + if (0 != put_user((kargs.page_table_dump - (u32 *)kargs.buffer) + (u32 *)uargs->buffer, &uargs->page_table_dump)) goto err_exit; + if (0 != put_user(kargs.register_writes_size, &uargs->register_writes_size)) goto err_exit; + if (0 != put_user(kargs.page_table_dump_size, &uargs->page_table_dump_size)) goto err_exit; + rc = 0; + +err_exit: + if (kargs.buffer) _mali_osk_vfree(kargs.buffer); + return rc; +} diff --git a/drivers/gpu/arm/mali/linux/mali_ukk_pp.c b/drivers/gpu/arm/mali/linux/mali_ukk_pp.c new file mode 100644 index 00000000000000..e5126ec713d2f0 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_ukk_pp.c @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2010, 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include /* file system operations */ +#include /* user space access */ + +#include "mali_ukk.h" +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_session.h" +#include "mali_ukk_wrappers.h" + +int pp_start_job_wrapper(struct mali_session_data *session_data, _mali_uk_pp_start_job_s __user *uargs) +{ + _mali_osk_errcode_t err; + + /* If the job was started successfully, 0 is returned. If there was an error, but the job + * was started, we return -ENOENT. For anything else returned, the job was not started. */ + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session_data, -EINVAL); + + err = _mali_ukk_pp_start_job(session_data, uargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + return 0; +} + +int pp_and_gp_start_job_wrapper(struct mali_session_data *session_data, _mali_uk_pp_and_gp_start_job_s __user *uargs) +{ + _mali_osk_errcode_t err; + + /* If the jobs were started successfully, 0 is returned. If there was an error, but the + * jobs were started, we return -ENOENT. For anything else returned, the jobs were not + * started. */ + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session_data, -EINVAL); + + err = _mali_ukk_pp_and_gp_start_job(session_data, uargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + return 0; +} + +int pp_get_number_of_cores_wrapper(struct mali_session_data *session_data, _mali_uk_get_pp_number_of_cores_s __user *uargs) +{ + _mali_uk_get_pp_number_of_cores_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session_data, -EINVAL); + + kargs.ctx = session_data; + + err = _mali_ukk_get_pp_number_of_cores(&kargs); + if (_MALI_OSK_ERR_OK != err) { + return map_errcode(err); + } + + kargs.ctx = NULL; /* prevent kernel address to be returned to user space */ + if (0 != copy_to_user(uargs, &kargs, sizeof(_mali_uk_get_pp_number_of_cores_s))) { + return -EFAULT; + } + + return 0; +} + +int pp_get_core_version_wrapper(struct mali_session_data *session_data, _mali_uk_get_pp_core_version_s __user *uargs) +{ + _mali_uk_get_pp_core_version_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session_data, -EINVAL); + + kargs.ctx = session_data; + err = _mali_ukk_get_pp_core_version(&kargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + if (0 != put_user(kargs.version, &uargs->version)) return -EFAULT; + + return 0; +} + +int pp_disable_wb_wrapper(struct mali_session_data *session_data, _mali_uk_pp_disable_wb_s __user *uargs) +{ + _mali_uk_pp_disable_wb_s kargs; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session_data, -EINVAL); + + if (0 != copy_from_user(&kargs, uargs, sizeof(_mali_uk_pp_disable_wb_s))) return -EFAULT; + + kargs.ctx = session_data; + _mali_ukk_pp_job_disable_wb(&kargs); + + return 0; +} diff --git a/drivers/gpu/arm/mali/linux/mali_ukk_profiling.c b/drivers/gpu/arm/mali/linux/mali_ukk_profiling.c new file mode 100644 index 00000000000000..8423f28b7f553e --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_ukk_profiling.c @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include /* file system operations */ +#include /* user space access */ +#include + +#include "mali_ukk.h" +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_session.h" +#include "mali_ukk_wrappers.h" + +int profiling_start_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_start_s __user *uargs) +{ + _mali_uk_profiling_start_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + if (0 != copy_from_user(&kargs, uargs, sizeof(_mali_uk_profiling_start_s))) { + return -EFAULT; + } + + kargs.ctx = session_data; + err = _mali_ukk_profiling_start(&kargs); + if (_MALI_OSK_ERR_OK != err) { + return map_errcode(err); + } + + if (0 != put_user(kargs.limit, &uargs->limit)) { + return -EFAULT; + } + + return 0; +} + +int profiling_add_event_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_add_event_s __user *uargs) +{ + _mali_uk_profiling_add_event_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + if (0 != copy_from_user(&kargs, uargs, sizeof(_mali_uk_profiling_add_event_s))) { + return -EFAULT; + } + + kargs.ctx = session_data; + err = _mali_ukk_profiling_add_event(&kargs); + if (_MALI_OSK_ERR_OK != err) { + return map_errcode(err); + } + + return 0; +} + +int profiling_stop_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_stop_s __user *uargs) +{ + _mali_uk_profiling_stop_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + kargs.ctx = session_data; + err = _mali_ukk_profiling_stop(&kargs); + if (_MALI_OSK_ERR_OK != err) { + return map_errcode(err); + } + + if (0 != put_user(kargs.count, &uargs->count)) { + return -EFAULT; + } + + return 0; +} + +int profiling_get_event_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_get_event_s __user *uargs) +{ + _mali_uk_profiling_get_event_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + if (0 != get_user(kargs.index, &uargs->index)) { + return -EFAULT; + } + + kargs.ctx = session_data; + + err = _mali_ukk_profiling_get_event(&kargs); + if (_MALI_OSK_ERR_OK != err) { + return map_errcode(err); + } + + kargs.ctx = NULL; /* prevent kernel address to be returned to user space */ + if (0 != copy_to_user(uargs, &kargs, sizeof(_mali_uk_profiling_get_event_s))) { + return -EFAULT; + } + + return 0; +} + +int profiling_clear_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_clear_s __user *uargs) +{ + _mali_uk_profiling_clear_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + kargs.ctx = session_data; + err = _mali_ukk_profiling_clear(&kargs); + if (_MALI_OSK_ERR_OK != err) { + return map_errcode(err); + } + + return 0; +} + +int profiling_report_sw_counters_wrapper(struct mali_session_data *session_data, _mali_uk_sw_counters_report_s __user *uargs) +{ + _mali_uk_sw_counters_report_s kargs; + _mali_osk_errcode_t err; + u32 *counter_buffer; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + if (0 != copy_from_user(&kargs, uargs, sizeof(_mali_uk_sw_counters_report_s))) { + return -EFAULT; + } + + /* make sure that kargs.num_counters is [at least somewhat] sane */ + if (kargs.num_counters > 10000) { + MALI_DEBUG_PRINT(1, ("User space attempted to allocate too many counters.\n")); + return -EINVAL; + } + + counter_buffer = (u32*)kmalloc(sizeof(u32) * kargs.num_counters, GFP_KERNEL); + if (NULL == counter_buffer) { + return -ENOMEM; + } + + if (0 != copy_from_user(counter_buffer, kargs.counters, sizeof(u32) * kargs.num_counters)) { + kfree(counter_buffer); + return -EFAULT; + } + + kargs.ctx = session_data; + kargs.counters = counter_buffer; + + err = _mali_ukk_sw_counters_report(&kargs); + + kfree(counter_buffer); + + if (_MALI_OSK_ERR_OK != err) { + return map_errcode(err); + } + + return 0; +} + + diff --git a/drivers/gpu/arm/mali/linux/mali_ukk_soft_job.c b/drivers/gpu/arm/mali/linux/mali_ukk_soft_job.c new file mode 100644 index 00000000000000..51ef4664787218 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_ukk_soft_job.c @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include /* file system operations */ +#include /* user space access */ + +#include "mali_ukk.h" +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_session.h" +#include "mali_ukk_wrappers.h" + +#include "mali_soft_job.h" +#include "mali_timeline.h" + +int soft_job_start_wrapper(struct mali_session_data *session, _mali_uk_soft_job_start_s __user *uargs) +{ + u32 type, user_job, point; + _mali_uk_fence_t uk_fence; + struct mali_timeline_fence fence; + struct mali_soft_job *job = NULL; + u32 __user *job_id_ptr = NULL; + + /* If the job was started successfully, 0 is returned. If there was an error, but the job + * was started, we return -ENOENT. For anything else returned, the job was not started. */ + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session, -EINVAL); + + MALI_DEBUG_ASSERT_POINTER(session->soft_job_system); + + if (0 != get_user(type, &uargs->type)) return -EFAULT; + if (0 != get_user(user_job, &uargs->user_job)) return -EFAULT; + if (0 != get_user(job_id_ptr, &uargs->job_id_ptr)) return -EFAULT; + + if (0 != copy_from_user(&uk_fence, &uargs->fence, sizeof(_mali_uk_fence_t))) return -EFAULT; + mali_timeline_fence_copy_uk_fence(&fence, &uk_fence); + + if (MALI_SOFT_JOB_TYPE_USER_SIGNALED < type) { + MALI_DEBUG_PRINT_ERROR(("Invalid soft job type specified\n")); + return -EINVAL; + } + + /* Create soft job. */ + job = mali_soft_job_create(session->soft_job_system, (enum mali_soft_job_type)type, user_job); + if (unlikely(NULL == job)) { + return map_errcode(_MALI_OSK_ERR_NOMEM); + } + + /* Write job id back to user space. */ + if (0 != put_user(job->id, job_id_ptr)) { + MALI_PRINT_ERROR(("Mali Soft Job: failed to put job id")); + mali_soft_job_destroy(job); + return map_errcode(_MALI_OSK_ERR_NOMEM); + } + + /* Start soft job. */ + point = mali_soft_job_start(job, &fence); + + if (0 != put_user(point, &uargs->point)) { + /* Let user space know that something failed after the job was started. */ + return -ENOENT; + } + + return 0; +} + +int soft_job_signal_wrapper(struct mali_session_data *session, _mali_uk_soft_job_signal_s __user *uargs) +{ + u32 job_id; + _mali_osk_errcode_t err; + + MALI_DEBUG_ASSERT_POINTER(session); + + if (0 != get_user(job_id, &uargs->job_id)) return -EFAULT; + + err = mali_soft_job_system_signal_job(session->soft_job_system, job_id); + + return map_errcode(err); +} diff --git a/drivers/gpu/arm/mali/linux/mali_ukk_timeline.c b/drivers/gpu/arm/mali/linux/mali_ukk_timeline.c new file mode 100644 index 00000000000000..dd72dee86970b9 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_ukk_timeline.c @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include /* file system operations */ +#include /* user space access */ + +#include "mali_ukk.h" +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_session.h" +#include "mali_ukk_wrappers.h" + +#include "mali_timeline.h" +#include "mali_timeline_fence_wait.h" +#include "mali_timeline_sync_fence.h" + +int timeline_get_latest_point_wrapper(struct mali_session_data *session, _mali_uk_timeline_get_latest_point_s __user *uargs) +{ + u32 val; + mali_timeline_id timeline; + mali_timeline_point point; + + MALI_DEBUG_ASSERT_POINTER(session); + + if (0 != get_user(val, &uargs->timeline)) return -EFAULT; + + if (MALI_UK_TIMELINE_MAX <= val) { + return -EINVAL; + } + + timeline = (mali_timeline_id)val; + + point = mali_timeline_system_get_latest_point(session->timeline_system, timeline); + + if (0 != put_user(point, &uargs->point)) return -EFAULT; + + return 0; +} + +int timeline_wait_wrapper(struct mali_session_data *session, _mali_uk_timeline_wait_s __user *uargs) +{ + u32 timeout, status; + mali_bool ret; + _mali_uk_fence_t uk_fence; + struct mali_timeline_fence fence; + + MALI_DEBUG_ASSERT_POINTER(session); + + if (0 != copy_from_user(&uk_fence, &uargs->fence, sizeof(_mali_uk_fence_t))) return -EFAULT; + if (0 != get_user(timeout, &uargs->timeout)) return -EFAULT; + + mali_timeline_fence_copy_uk_fence(&fence, &uk_fence); + + ret = mali_timeline_fence_wait(session->timeline_system, &fence, timeout); + status = (MALI_TRUE == ret ? 1 : 0); + + if (0 != put_user(status, &uargs->status)) return -EFAULT; + + return 0; +} + +int timeline_create_sync_fence_wrapper(struct mali_session_data *session, _mali_uk_timeline_create_sync_fence_s __user *uargs) +{ + s32 sync_fd = -1; + _mali_uk_fence_t uk_fence; + struct mali_timeline_fence fence; + + MALI_DEBUG_ASSERT_POINTER(session); + + if (0 != copy_from_user(&uk_fence, &uargs->fence, sizeof(_mali_uk_fence_t))) return -EFAULT; + mali_timeline_fence_copy_uk_fence(&fence, &uk_fence); + +#if defined(CONFIG_SYNC) + sync_fd = mali_timeline_sync_fence_create(session->timeline_system, &fence); +#else + sync_fd = -1; +#endif /* defined(CONFIG_SYNC) */ + + if (0 != put_user(sync_fd, &uargs->sync_fd)) return -EFAULT; + + return 0; +} diff --git a/drivers/gpu/arm/mali/linux/mali_ukk_vsync.c b/drivers/gpu/arm/mali/linux/mali_ukk_vsync.c new file mode 100644 index 00000000000000..276a147bfdc5b7 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_ukk_vsync.c @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2011-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include /* file system operations */ +#include /* user space access */ + +#include "mali_ukk.h" +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_session.h" +#include "mali_ukk_wrappers.h" + + +int vsync_event_report_wrapper(struct mali_session_data *session_data, _mali_uk_vsync_event_report_s __user *uargs) +{ + _mali_uk_vsync_event_report_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + if (0 != copy_from_user(&kargs, uargs, sizeof(_mali_uk_vsync_event_report_s))) { + return -EFAULT; + } + + kargs.ctx = session_data; + err = _mali_ukk_vsync_event_report(&kargs); + if (_MALI_OSK_ERR_OK != err) { + return map_errcode(err); + } + + return 0; +} + diff --git a/drivers/gpu/arm/mali/linux/mali_ukk_wrappers.h b/drivers/gpu/arm/mali/linux/mali_ukk_wrappers.h new file mode 100644 index 00000000000000..0883b6c0d7b7cc --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_ukk_wrappers.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_ukk_wrappers.h + * Defines the wrapper functions for each user-kernel function + */ + +#ifndef __MALI_UKK_WRAPPERS_H__ +#define __MALI_UKK_WRAPPERS_H__ + +#include "mali_uk_types.h" +#include "mali_osk.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int wait_for_notification_wrapper(struct mali_session_data *session_data, _mali_uk_wait_for_notification_s __user *uargs); +int get_api_version_wrapper(struct mali_session_data *session_data, _mali_uk_get_api_version_s __user *uargs); +int get_user_settings_wrapper(struct mali_session_data *session_data, _mali_uk_get_user_settings_s __user *uargs); +int post_notification_wrapper(struct mali_session_data *session_data, _mali_uk_post_notification_s __user *uargs); +int request_high_priority_wrapper(struct mali_session_data *session_data, _mali_uk_request_high_priority_s __user *uargs); + +int mem_write_safe_wrapper(struct mali_session_data *session_data, _mali_uk_mem_write_safe_s __user * uargs); +int mem_map_ext_wrapper(struct mali_session_data *session_data, _mali_uk_map_external_mem_s __user * argument); +int mem_unmap_ext_wrapper(struct mali_session_data *session_data, _mali_uk_unmap_external_mem_s __user * argument); +int mem_query_mmu_page_table_dump_size_wrapper(struct mali_session_data *session_data, _mali_uk_query_mmu_page_table_dump_size_s __user * uargs); +int mem_dump_mmu_page_table_wrapper(struct mali_session_data *session_data, _mali_uk_dump_mmu_page_table_s __user * uargs); + +int timeline_get_latest_point_wrapper(struct mali_session_data *session, _mali_uk_timeline_get_latest_point_s __user *uargs); +int timeline_wait_wrapper(struct mali_session_data *session, _mali_uk_timeline_wait_s __user *uargs); +int timeline_create_sync_fence_wrapper(struct mali_session_data *session, _mali_uk_timeline_create_sync_fence_s __user *uargs); +int soft_job_start_wrapper(struct mali_session_data *session, _mali_uk_soft_job_start_s __user *uargs); +int soft_job_signal_wrapper(struct mali_session_data *session, _mali_uk_soft_job_signal_s __user *uargs); + +#if defined(CONFIG_MALI400_UMP) +int mem_attach_ump_wrapper(struct mali_session_data *session_data, _mali_uk_attach_ump_mem_s __user * argument); +int mem_release_ump_wrapper(struct mali_session_data *session_data, _mali_uk_release_ump_mem_s __user * argument); +#endif + +int pp_start_job_wrapper(struct mali_session_data *session_data, _mali_uk_pp_start_job_s __user *uargs); +int pp_and_gp_start_job_wrapper(struct mali_session_data *session_data, _mali_uk_pp_and_gp_start_job_s __user *uargs); +int pp_get_number_of_cores_wrapper(struct mali_session_data *session_data, _mali_uk_get_pp_number_of_cores_s __user *uargs); +int pp_get_core_version_wrapper(struct mali_session_data *session_data, _mali_uk_get_pp_core_version_s __user *uargs); +int pp_disable_wb_wrapper(struct mali_session_data *session_data, _mali_uk_pp_disable_wb_s __user *uargs); +int gp_start_job_wrapper(struct mali_session_data *session_data, _mali_uk_gp_start_job_s __user *uargs); +int gp_get_number_of_cores_wrapper(struct mali_session_data *session_data, _mali_uk_get_gp_number_of_cores_s __user *uargs); +int gp_get_core_version_wrapper(struct mali_session_data *session_data, _mali_uk_get_gp_core_version_s __user *uargs); +int gp_suspend_response_wrapper(struct mali_session_data *session_data, _mali_uk_gp_suspend_response_s __user *uargs); + +int profiling_start_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_start_s __user *uargs); +int profiling_add_event_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_add_event_s __user *uargs); +int profiling_stop_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_stop_s __user *uargs); +int profiling_get_event_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_get_event_s __user *uargs); +int profiling_clear_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_clear_s __user *uargs); +int profiling_report_sw_counters_wrapper(struct mali_session_data *session_data, _mali_uk_sw_counters_report_s __user *uargs); + +int vsync_event_report_wrapper(struct mali_session_data *session_data, _mali_uk_vsync_event_report_s __user *uargs); + + +int map_errcode( _mali_osk_errcode_t err ); + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_UKK_WRAPPERS_H__ */ diff --git a/drivers/gpu/arm/mali/platform/arm/arm.c b/drivers/gpu/arm/mali/platform/arm/arm.c new file mode 100644 index 00000000000000..7ab66ce3aa5305 --- /dev/null +++ b/drivers/gpu/arm/mali/platform/arm/arm.c @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2010, 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_platform.c + * Platform specific Mali driver functions for: + * - Realview Versatile platforms with ARM11 Mpcore and virtex 5. + * - Versatile Express platforms with ARM Cortex-A9 and virtex 6. + */ +#include +#include +#include +#ifdef CONFIG_PM_RUNTIME +#include +#endif +#include +#include +#include "mali_kernel_common.h" +#include +#include + +#include "arm_core_scaling.h" +#include "mali_pp_scheduler.h" + +static void mali_platform_device_release(struct device *device); +static u32 mali_read_phys(u32 phys_addr); +#if defined(CONFIG_ARCH_REALVIEW) +static void mali_write_phys(u32 phys_addr, u32 value); +#endif + +static int mali_core_scaling_enable = 1; + +void mali_gpu_utilization_callback(struct mali_gpu_utilization_data *data); + +#if defined(CONFIG_ARCH_VEXPRESS) + +static struct resource mali_gpu_resources_m450_mp8[] = { + MALI_GPU_RESOURCES_MALI450_MP8_PMU(0xFC040000, -1, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 68) +}; + +#elif defined(CONFIG_ARCH_REALVIEW) + +static struct resource mali_gpu_resources_m300[] = { + MALI_GPU_RESOURCES_MALI300_PMU(0xC0000000, -1, -1, -1, -1) +}; + +static struct resource mali_gpu_resources_m400_mp1[] = { + MALI_GPU_RESOURCES_MALI400_MP1_PMU(0xC0000000, -1, -1, -1, -1) +}; + +static struct resource mali_gpu_resources_m400_mp2[] = { + MALI_GPU_RESOURCES_MALI400_MP2_PMU(0xC0000000, -1, -1, -1, -1, -1, -1) +}; + +#endif + +static struct mali_gpu_device_data mali_gpu_data = { +#if defined(CONFIG_ARCH_VEXPRESS) + .shared_mem_size =256 * 1024 * 1024, /* 256MB */ +#elif defined(CONFIG_ARCH_REALVIEW) + .dedicated_mem_start = 0x80000000, /* Physical start address (use 0xD0000000 for old indirect setup) */ + .dedicated_mem_size = 0x10000000, /* 256MB */ +#endif + .fb_start = 0xe0000000, + .fb_size = 0x01000000, + .max_job_runtime = 60000, /* 60 seconds */ + .utilization_interval = 1000, /* 1000ms */ + .utilization_callback = mali_gpu_utilization_callback, + .pmu_switch_delay = 0xFF, /* do not have to be this high on FPGA, but it is good for testing to have a delay */ + .pmu_domain_config = {0x1, 0x2, 0x4, 0x4, 0x4, 0x8, 0x8, 0x8, 0x8, 0x1, 0x2, 0x8}, +}; + +static struct platform_device mali_gpu_device = { + .name = MALI_GPU_NAME_UTGARD, + .id = 0, + .dev.release = mali_platform_device_release, + .dev.coherent_dma_mask = DMA_BIT_MASK(32), + + .dev.platform_data = &mali_gpu_data, +}; + +int mali_platform_device_register(void) +{ + int err = -1; + int num_pp_cores = 0; +#if defined(CONFIG_ARCH_REALVIEW) + u32 m400_gp_version; +#endif + + MALI_DEBUG_PRINT(4, ("mali_platform_device_register() called\n")); + + /* Detect present Mali GPU and connect the correct resources to the device */ +#if defined(CONFIG_ARCH_VEXPRESS) + + if (mali_read_phys(0xFC020000) == 0x00010100) { + MALI_DEBUG_PRINT(4, ("Registering Mali-450 MP8 device\n")); + num_pp_cores = 8; + mali_gpu_device.num_resources = ARRAY_SIZE(mali_gpu_resources_m450_mp8); + mali_gpu_device.resource = mali_gpu_resources_m450_mp8; + } + +#elif defined(CONFIG_ARCH_REALVIEW) + + m400_gp_version = mali_read_phys(0xC000006C); + if ((m400_gp_version & 0xFFFF0000) == 0x0C070000) { + MALI_DEBUG_PRINT(4, ("Registering Mali-300 device\n")); + num_pp_cores = 1; + mali_gpu_device.num_resources = ARRAY_SIZE(mali_gpu_resources_m300); + mali_gpu_device.resource = mali_gpu_resources_m300; + mali_write_phys(0xC0010020, 0xA); /* Enable direct memory mapping for FPGA */ + } else if ((m400_gp_version & 0xFFFF0000) == 0x0B070000) { + u32 fpga_fw_version = mali_read_phys(0xC0010000); + if (fpga_fw_version == 0x130C008F || fpga_fw_version == 0x110C008F) { + /* Mali-400 MP1 r1p0 or r1p1 */ + MALI_DEBUG_PRINT(4, ("Registering Mali-400 MP1 device\n")); + num_pp_cores = 1; + mali_gpu_device.num_resources = ARRAY_SIZE(mali_gpu_resources_m400_mp1); + mali_gpu_device.resource = mali_gpu_resources_m400_mp1; + mali_write_phys(0xC0010020, 0xA); /* Enable direct memory mapping for FPGA */ + } else if (fpga_fw_version == 0x130C000F) { + /* Mali-400 MP2 r1p1 */ + MALI_DEBUG_PRINT(4, ("Registering Mali-400 MP2 device\n")); + num_pp_cores = 2; + mali_gpu_device.num_resources = ARRAY_SIZE(mali_gpu_resources_m400_mp2); + mali_gpu_device.resource = mali_gpu_resources_m400_mp2; + mali_write_phys(0xC0010020, 0xA); /* Enable direct memory mapping for FPGA */ + } + } + +#endif + /* Register the platform device */ + err = platform_device_register(&mali_gpu_device); + if (0 == err) { +#ifdef CONFIG_PM_RUNTIME +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) + pm_runtime_set_autosuspend_delay(&(mali_gpu_device.dev), 1000); + pm_runtime_use_autosuspend(&(mali_gpu_device.dev)); +#endif + pm_runtime_enable(&(mali_gpu_device.dev)); +#endif + MALI_DEBUG_ASSERT(0 < num_pp_cores); + mali_core_scaling_init(num_pp_cores); + + return 0; + } + + return err; +} + +void mali_platform_device_unregister(void) +{ + MALI_DEBUG_PRINT(4, ("mali_platform_device_unregister() called\n")); + + mali_core_scaling_term(); + platform_device_unregister(&mali_gpu_device); + + platform_device_put(&mali_gpu_device); + +#if defined(CONFIG_ARCH_REALVIEW) + mali_write_phys(0xC0010020, 0x9); /* Restore default (legacy) memory mapping */ +#endif +} + +static void mali_platform_device_release(struct device *device) +{ + MALI_DEBUG_PRINT(4, ("mali_platform_device_release() called\n")); +} + +static u32 mali_read_phys(u32 phys_addr) +{ + u32 phys_addr_page = phys_addr & 0xFFFFE000; + u32 phys_offset = phys_addr & 0x00001FFF; + u32 map_size = phys_offset + sizeof(u32); + u32 ret = 0xDEADBEEF; + void *mem_mapped = ioremap_nocache(phys_addr_page, map_size); + if (NULL != mem_mapped) { + ret = (u32)ioread32(((u8*)mem_mapped) + phys_offset); + iounmap(mem_mapped); + } + + return ret; +} + +#if defined(CONFIG_ARCH_REALVIEW) +static void mali_write_phys(u32 phys_addr, u32 value) +{ + u32 phys_addr_page = phys_addr & 0xFFFFE000; + u32 phys_offset = phys_addr & 0x00001FFF; + u32 map_size = phys_offset + sizeof(u32); + void *mem_mapped = ioremap_nocache(phys_addr_page, map_size); + if (NULL != mem_mapped) { + iowrite32(value, ((u8*)mem_mapped) + phys_offset); + iounmap(mem_mapped); + } +} +#endif + +static int param_set_core_scaling(const char *val, const struct kernel_param *kp) +{ + int ret = param_set_int(val, kp); + + if (1 == mali_core_scaling_enable) { + mali_core_scaling_sync(mali_pp_scheduler_get_num_cores_enabled()); + } + return ret; +} + +static struct kernel_param_ops param_ops_core_scaling = { + .set = param_set_core_scaling, + .get = param_get_int, +}; + +module_param_cb(mali_core_scaling_enable, ¶m_ops_core_scaling, &mali_core_scaling_enable, 0644); +MODULE_PARM_DESC(mali_core_scaling_enable, "1 means to enable core scaling policy, 0 means to disable core scaling policy"); + +void mali_gpu_utilization_callback(struct mali_gpu_utilization_data *data) +{ + if (1 == mali_core_scaling_enable) { + mali_core_scaling_update(data); + } +} diff --git a/drivers/gpu/arm/mali/platform/arm/arm_core_scaling.c b/drivers/gpu/arm/mali/platform/arm/arm_core_scaling.c new file mode 100644 index 00000000000000..b5dbdc9f27099f --- /dev/null +++ b/drivers/gpu/arm/mali/platform/arm/arm_core_scaling.c @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file arm_core_scaling.c + * Example core scaling policy. + */ + +#include "arm_core_scaling.h" + +#include +#include "mali_kernel_common.h" + +#include + +static int num_cores_total; +static int num_cores_enabled; + +static struct work_struct wq_work; + +static void set_num_cores(struct work_struct *work) +{ + int err = mali_perf_set_num_pp_cores(num_cores_enabled); + MALI_DEBUG_ASSERT(0 == err); + MALI_IGNORE(err); +} + +static void enable_one_core(void) +{ + if (num_cores_enabled < num_cores_total) { + ++num_cores_enabled; + schedule_work(&wq_work); + MALI_DEBUG_PRINT(3, ("Core scaling: Enabling one more core\n")); + } + + MALI_DEBUG_ASSERT( 1 <= num_cores_enabled); + MALI_DEBUG_ASSERT(num_cores_total >= num_cores_enabled); +} + +static void disable_one_core(void) +{ + if (1 < num_cores_enabled) { + --num_cores_enabled; + schedule_work(&wq_work); + MALI_DEBUG_PRINT(3, ("Core scaling: Disabling one core\n")); + } + + MALI_DEBUG_ASSERT( 1 <= num_cores_enabled); + MALI_DEBUG_ASSERT(num_cores_total >= num_cores_enabled); +} + +static void enable_max_num_cores(void) +{ + if (num_cores_enabled < num_cores_total) { + num_cores_enabled = num_cores_total; + schedule_work(&wq_work); + MALI_DEBUG_PRINT(3, ("Core scaling: Enabling maximum number of cores\n")); + } + + MALI_DEBUG_ASSERT(num_cores_total == num_cores_enabled); +} + +void mali_core_scaling_init(int num_pp_cores) +{ + INIT_WORK(&wq_work, set_num_cores); + + num_cores_total = num_pp_cores; + num_cores_enabled = num_pp_cores; + + /* NOTE: Mali is not fully initialized at this point. */ +} + +void mali_core_scaling_sync(int num_cores) +{ + num_cores_enabled = num_cores; +} + +void mali_core_scaling_term(void) +{ + flush_scheduled_work(); +} + +#define PERCENT_OF(percent, max) ((int) ((percent)*(max)/100.0 + 0.5)) + +void mali_core_scaling_update(struct mali_gpu_utilization_data *data) +{ + /* + * This function implements a very trivial PP core scaling algorithm. + * + * It is _NOT_ of production quality. + * The only intention behind this algorithm is to exercise and test the + * core scaling functionality of the driver. + * It is _NOT_ tuned for neither power saving nor performance! + * + * Other metrics than PP utilization need to be considered as well + * in order to make a good core scaling algorithm. + */ + + MALI_DEBUG_PRINT(3, ("Utilization: (%3d, %3d, %3d), cores enabled: %d/%d\n", data->utilization_gpu, data->utilization_gp, data->utilization_pp, num_cores_enabled, num_cores_total)); + + /* NOTE: this function is normally called directly from the utilization callback which is in + * timer context. */ + + if ( PERCENT_OF(90, 256) < data->utilization_pp) { + enable_max_num_cores(); + } else if (PERCENT_OF(50, 256) < data->utilization_pp) { + enable_one_core(); + } else if (PERCENT_OF(40, 256) < data->utilization_pp) { + /* do nothing */ + } else if (PERCENT_OF( 0, 256) < data->utilization_pp) { + disable_one_core(); + } else { + /* do nothing */ + } +} diff --git a/drivers/gpu/arm/mali/platform/arm/arm_core_scaling.h b/drivers/gpu/arm/mali/platform/arm/arm_core_scaling.h new file mode 100644 index 00000000000000..193f43cc777890 --- /dev/null +++ b/drivers/gpu/arm/mali/platform/arm/arm_core_scaling.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file arm_core_scaling.h + * Example core scaling policy. + */ + +#ifndef __ARM_CORE_SCALING_H__ +#define __ARM_CORE_SCALING_H__ + +struct mali_gpu_utilization_data; + +/** + * Initialize core scaling policy. + * + * @note The core scaling policy will assume that all PP cores are on initially. + * + * @param num_pp_cores Total number of PP cores. + */ +void mali_core_scaling_init(int num_pp_cores); + +/** + * Terminate core scaling policy. + */ +void mali_core_scaling_term(void); + +/** + * Update core scaling policy with new utilization data. + * + * @param data Utilization data. + */ +void mali_core_scaling_update(struct mali_gpu_utilization_data *data); + +void mali_core_scaling_sync(int num_cores); + +#endif /* __ARM_CORE_SCALING_H__ */ diff --git a/drivers/gpu/arm/mali/readme.txt b/drivers/gpu/arm/mali/readme.txt new file mode 100644 index 00000000000000..26095066bb42b4 --- /dev/null +++ b/drivers/gpu/arm/mali/readme.txt @@ -0,0 +1,24 @@ +Building the Mali Device Driver for Linux +----------------------------------------- + +Build the Mali Device Driver for Linux by running the following make command: + +KDIR= USING_UMP= BUILD= make + +where + kdir_path: Path to your Linux Kernel directory + ump_option: 1 = Enable UMP support(*) + 0 = disable UMP support + build_option: debug = debug build of driver + release = release build of driver + +(*) For newer Linux Kernels, the Module.symvers file for the UMP device driver + must be available. The UMP_SYMVERS_FILE variable in the Makefile should + point to this file. This file is generated when the UMP driver is built. + +The result will be a mali.ko file, which can be loaded into the Linux kernel +by using the insmod command. + +The kernel needs to be provided with a platform_device struct for the Mali GPU +device. See the mali_utgard.h header file for how to set up the Mali GPU +resources. diff --git a/drivers/gpu/arm/mali/regs/mali_200_regs.h b/drivers/gpu/arm/mali/regs/mali_200_regs.h new file mode 100644 index 00000000000000..31c0d827a65489 --- /dev/null +++ b/drivers/gpu/arm/mali/regs/mali_200_regs.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2010, 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _MALI200_REGS_H_ +#define _MALI200_REGS_H_ + +/** + * Enum for management register addresses. + */ +enum mali200_mgmt_reg { + MALI200_REG_ADDR_MGMT_VERSION = 0x1000, + MALI200_REG_ADDR_MGMT_CURRENT_REND_LIST_ADDR = 0x1004, + MALI200_REG_ADDR_MGMT_STATUS = 0x1008, + MALI200_REG_ADDR_MGMT_CTRL_MGMT = 0x100c, + + MALI200_REG_ADDR_MGMT_INT_RAWSTAT = 0x1020, + MALI200_REG_ADDR_MGMT_INT_CLEAR = 0x1024, + MALI200_REG_ADDR_MGMT_INT_MASK = 0x1028, + MALI200_REG_ADDR_MGMT_INT_STATUS = 0x102c, + + MALI200_REG_ADDR_MGMT_WRITE_BOUNDARY_LOW = 0x1044, + + MALI200_REG_ADDR_MGMT_BUS_ERROR_STATUS = 0x1050, + + MALI200_REG_ADDR_MGMT_PERF_CNT_0_ENABLE = 0x1080, + MALI200_REG_ADDR_MGMT_PERF_CNT_0_SRC = 0x1084, + MALI200_REG_ADDR_MGMT_PERF_CNT_0_VALUE = 0x108c, + + MALI200_REG_ADDR_MGMT_PERF_CNT_1_ENABLE = 0x10a0, + MALI200_REG_ADDR_MGMT_PERF_CNT_1_SRC = 0x10a4, + MALI200_REG_ADDR_MGMT_PERF_CNT_1_VALUE = 0x10ac, + + MALI200_REG_ADDR_MGMT_PERFMON_CONTR = 0x10b0, + MALI200_REG_ADDR_MGMT_PERFMON_BASE = 0x10b4, + + MALI200_REG_SIZEOF_REGISTER_BANK = 0x10f0 + +}; + +#define MALI200_REG_VAL_PERF_CNT_ENABLE 1 + +enum mali200_mgmt_ctrl_mgmt { + MALI200_REG_VAL_CTRL_MGMT_STOP_BUS = (1<<0), + MALI200_REG_VAL_CTRL_MGMT_FLUSH_CACHES = (1<<3), + MALI200_REG_VAL_CTRL_MGMT_FORCE_RESET = (1<<5), + MALI200_REG_VAL_CTRL_MGMT_START_RENDERING = (1<<6), + MALI400PP_REG_VAL_CTRL_MGMT_SOFT_RESET = (1<<7), /* Only valid for Mali-300 and later */ +}; + +enum mali200_mgmt_irq { + MALI200_REG_VAL_IRQ_END_OF_FRAME = (1<<0), + MALI200_REG_VAL_IRQ_END_OF_TILE = (1<<1), + MALI200_REG_VAL_IRQ_HANG = (1<<2), + MALI200_REG_VAL_IRQ_FORCE_HANG = (1<<3), + MALI200_REG_VAL_IRQ_BUS_ERROR = (1<<4), + MALI200_REG_VAL_IRQ_BUS_STOP = (1<<5), + MALI200_REG_VAL_IRQ_CNT_0_LIMIT = (1<<6), + MALI200_REG_VAL_IRQ_CNT_1_LIMIT = (1<<7), + MALI200_REG_VAL_IRQ_WRITE_BOUNDARY_ERROR = (1<<8), + MALI400PP_REG_VAL_IRQ_INVALID_PLIST_COMMAND = (1<<9), + MALI400PP_REG_VAL_IRQ_CALL_STACK_UNDERFLOW = (1<<10), + MALI400PP_REG_VAL_IRQ_CALL_STACK_OVERFLOW = (1<<11), + MALI400PP_REG_VAL_IRQ_RESET_COMPLETED = (1<<12), +}; + +#define MALI200_REG_VAL_IRQ_MASK_ALL ((enum mali200_mgmt_irq) (\ + MALI200_REG_VAL_IRQ_END_OF_FRAME |\ + MALI200_REG_VAL_IRQ_END_OF_TILE |\ + MALI200_REG_VAL_IRQ_HANG |\ + MALI200_REG_VAL_IRQ_FORCE_HANG |\ + MALI200_REG_VAL_IRQ_BUS_ERROR |\ + MALI200_REG_VAL_IRQ_BUS_STOP |\ + MALI200_REG_VAL_IRQ_CNT_0_LIMIT |\ + MALI200_REG_VAL_IRQ_CNT_1_LIMIT |\ + MALI200_REG_VAL_IRQ_WRITE_BOUNDARY_ERROR |\ + MALI400PP_REG_VAL_IRQ_INVALID_PLIST_COMMAND |\ + MALI400PP_REG_VAL_IRQ_CALL_STACK_UNDERFLOW |\ + MALI400PP_REG_VAL_IRQ_CALL_STACK_OVERFLOW |\ + MALI400PP_REG_VAL_IRQ_RESET_COMPLETED)) + +#define MALI200_REG_VAL_IRQ_MASK_USED ((enum mali200_mgmt_irq) (\ + MALI200_REG_VAL_IRQ_END_OF_FRAME |\ + MALI200_REG_VAL_IRQ_FORCE_HANG |\ + MALI200_REG_VAL_IRQ_BUS_ERROR |\ + MALI200_REG_VAL_IRQ_WRITE_BOUNDARY_ERROR |\ + MALI400PP_REG_VAL_IRQ_INVALID_PLIST_COMMAND |\ + MALI400PP_REG_VAL_IRQ_CALL_STACK_UNDERFLOW |\ + MALI400PP_REG_VAL_IRQ_CALL_STACK_OVERFLOW)) + +#define MALI200_REG_VAL_IRQ_MASK_NONE ((enum mali200_mgmt_irq)(0)) + +enum mali200_mgmt_status { + MALI200_REG_VAL_STATUS_RENDERING_ACTIVE = (1<<0), + MALI200_REG_VAL_STATUS_BUS_STOPPED = (1<<4), +}; + +enum mali200_render_unit { + MALI200_REG_ADDR_FRAME = 0x0000, + MALI200_REG_ADDR_RSW = 0x0004, + MALI200_REG_ADDR_STACK = 0x0030, + MALI200_REG_ADDR_STACK_SIZE = 0x0034, + MALI200_REG_ADDR_ORIGIN_OFFSET_X = 0x0040 +}; + +enum mali200_wb_unit { + MALI200_REG_ADDR_WB0 = 0x0100, + MALI200_REG_ADDR_WB1 = 0x0200, + MALI200_REG_ADDR_WB2 = 0x0300 +}; + +enum mali200_wb_unit_regs { + MALI200_REG_ADDR_WB_SOURCE_SELECT = 0x0000, + MALI200_REG_ADDR_WB_SOURCE_ADDR = 0x0004, +}; + +/* This should be in the top 16 bit of the version register of Mali PP */ +#define MALI200_PP_PRODUCT_ID 0xC807 +#define MALI300_PP_PRODUCT_ID 0xCE07 +#define MALI400_PP_PRODUCT_ID 0xCD07 +#define MALI450_PP_PRODUCT_ID 0xCF07 + + +#endif /* _MALI200_REGS_H_ */ diff --git a/drivers/gpu/arm/mali/regs/mali_gp_regs.h b/drivers/gpu/arm/mali/regs/mali_gp_regs.h new file mode 100644 index 00000000000000..04598e56ca7ebd --- /dev/null +++ b/drivers/gpu/arm/mali/regs/mali_gp_regs.h @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2010, 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _MALIGP2_CONROL_REGS_H_ +#define _MALIGP2_CONROL_REGS_H_ + +/** + * These are the different geometry processor control registers. + * Their usage is to control and monitor the operation of the + * Vertex Shader and the Polygon List Builder in the geometry processor. + * Addresses are in 32-bit word relative sizes. + * @see [P0081] "Geometry Processor Data Structures" for details + */ + +typedef enum { + MALIGP2_REG_ADDR_MGMT_VSCL_START_ADDR = 0x00, + MALIGP2_REG_ADDR_MGMT_VSCL_END_ADDR = 0x04, + MALIGP2_REG_ADDR_MGMT_PLBUCL_START_ADDR = 0x08, + MALIGP2_REG_ADDR_MGMT_PLBUCL_END_ADDR = 0x0c, + MALIGP2_REG_ADDR_MGMT_PLBU_ALLOC_START_ADDR = 0x10, + MALIGP2_REG_ADDR_MGMT_PLBU_ALLOC_END_ADDR = 0x14, + MALIGP2_REG_ADDR_MGMT_CMD = 0x20, + MALIGP2_REG_ADDR_MGMT_INT_RAWSTAT = 0x24, + MALIGP2_REG_ADDR_MGMT_INT_CLEAR = 0x28, + MALIGP2_REG_ADDR_MGMT_INT_MASK = 0x2C, + MALIGP2_REG_ADDR_MGMT_INT_STAT = 0x30, + MALIGP2_REG_ADDR_MGMT_WRITE_BOUND_LOW = 0x34, + MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_ENABLE = 0x3C, + MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_ENABLE = 0x40, + MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_SRC = 0x44, + MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_SRC = 0x48, + MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_VALUE = 0x4C, + MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_VALUE = 0x50, + MALIGP2_REG_ADDR_MGMT_STATUS = 0x68, + MALIGP2_REG_ADDR_MGMT_VERSION = 0x6C, + MALIGP2_REG_ADDR_MGMT_VSCL_START_ADDR_READ = 0x80, + MALIGP2_REG_ADDR_MGMT_PLBCL_START_ADDR_READ = 0x84, + MALIGP2_CONTR_AXI_BUS_ERROR_STAT = 0x94, + MALIGP2_REGISTER_ADDRESS_SPACE_SIZE = 0x98, +} maligp_reg_addr_mgmt_addr; + +#define MALIGP2_REG_VAL_PERF_CNT_ENABLE 1 + +/** + * Commands to geometry processor. + * @see MALIGP2_CTRL_REG_CMD + */ +typedef enum { + MALIGP2_REG_VAL_CMD_START_VS = (1<< 0), + MALIGP2_REG_VAL_CMD_START_PLBU = (1<< 1), + MALIGP2_REG_VAL_CMD_UPDATE_PLBU_ALLOC = (1<< 4), + MALIGP2_REG_VAL_CMD_RESET = (1<< 5), + MALIGP2_REG_VAL_CMD_FORCE_HANG = (1<< 6), + MALIGP2_REG_VAL_CMD_STOP_BUS = (1<< 9), + MALI400GP_REG_VAL_CMD_SOFT_RESET = (1<<10), /* only valid for Mali-300 and later */ +} mgp_contr_reg_val_cmd; + + +/** @defgroup MALIGP2_IRQ + * Interrupt status of geometry processor. + * @see MALIGP2_CTRL_REG_INT_RAWSTAT, MALIGP2_REG_ADDR_MGMT_INT_CLEAR, + * MALIGP2_REG_ADDR_MGMT_INT_MASK, MALIGP2_REG_ADDR_MGMT_INT_STAT + * @{ + */ +#define MALIGP2_REG_VAL_IRQ_VS_END_CMD_LST (1 << 0) +#define MALIGP2_REG_VAL_IRQ_PLBU_END_CMD_LST (1 << 1) +#define MALIGP2_REG_VAL_IRQ_PLBU_OUT_OF_MEM (1 << 2) +#define MALIGP2_REG_VAL_IRQ_VS_SEM_IRQ (1 << 3) +#define MALIGP2_REG_VAL_IRQ_PLBU_SEM_IRQ (1 << 4) +#define MALIGP2_REG_VAL_IRQ_HANG (1 << 5) +#define MALIGP2_REG_VAL_IRQ_FORCE_HANG (1 << 6) +#define MALIGP2_REG_VAL_IRQ_PERF_CNT_0_LIMIT (1 << 7) +#define MALIGP2_REG_VAL_IRQ_PERF_CNT_1_LIMIT (1 << 8) +#define MALIGP2_REG_VAL_IRQ_WRITE_BOUND_ERR (1 << 9) +#define MALIGP2_REG_VAL_IRQ_SYNC_ERROR (1 << 10) +#define MALIGP2_REG_VAL_IRQ_AXI_BUS_ERROR (1 << 11) +#define MALI400GP_REG_VAL_IRQ_AXI_BUS_STOPPED (1 << 12) +#define MALI400GP_REG_VAL_IRQ_VS_INVALID_CMD (1 << 13) +#define MALI400GP_REG_VAL_IRQ_PLB_INVALID_CMD (1 << 14) +#define MALI400GP_REG_VAL_IRQ_RESET_COMPLETED (1 << 19) +#define MALI400GP_REG_VAL_IRQ_SEMAPHORE_UNDERFLOW (1 << 20) +#define MALI400GP_REG_VAL_IRQ_SEMAPHORE_OVERFLOW (1 << 21) +#define MALI400GP_REG_VAL_IRQ_PTR_ARRAY_OUT_OF_BOUNDS (1 << 22) + +/* Mask defining all IRQs in Mali GP */ +#define MALIGP2_REG_VAL_IRQ_MASK_ALL \ + (\ + MALIGP2_REG_VAL_IRQ_VS_END_CMD_LST | \ + MALIGP2_REG_VAL_IRQ_PLBU_END_CMD_LST | \ + MALIGP2_REG_VAL_IRQ_PLBU_OUT_OF_MEM | \ + MALIGP2_REG_VAL_IRQ_VS_SEM_IRQ | \ + MALIGP2_REG_VAL_IRQ_PLBU_SEM_IRQ | \ + MALIGP2_REG_VAL_IRQ_HANG | \ + MALIGP2_REG_VAL_IRQ_FORCE_HANG | \ + MALIGP2_REG_VAL_IRQ_PERF_CNT_0_LIMIT | \ + MALIGP2_REG_VAL_IRQ_PERF_CNT_1_LIMIT | \ + MALIGP2_REG_VAL_IRQ_WRITE_BOUND_ERR | \ + MALIGP2_REG_VAL_IRQ_SYNC_ERROR | \ + MALIGP2_REG_VAL_IRQ_AXI_BUS_ERROR | \ + MALI400GP_REG_VAL_IRQ_AXI_BUS_STOPPED | \ + MALI400GP_REG_VAL_IRQ_VS_INVALID_CMD | \ + MALI400GP_REG_VAL_IRQ_PLB_INVALID_CMD | \ + MALI400GP_REG_VAL_IRQ_RESET_COMPLETED | \ + MALI400GP_REG_VAL_IRQ_SEMAPHORE_UNDERFLOW | \ + MALI400GP_REG_VAL_IRQ_SEMAPHORE_OVERFLOW | \ + MALI400GP_REG_VAL_IRQ_PTR_ARRAY_OUT_OF_BOUNDS) + +/* Mask defining the IRQs in Mali GP which we use */ +#define MALIGP2_REG_VAL_IRQ_MASK_USED \ + (\ + MALIGP2_REG_VAL_IRQ_VS_END_CMD_LST | \ + MALIGP2_REG_VAL_IRQ_PLBU_END_CMD_LST | \ + MALIGP2_REG_VAL_IRQ_PLBU_OUT_OF_MEM | \ + MALIGP2_REG_VAL_IRQ_FORCE_HANG | \ + MALIGP2_REG_VAL_IRQ_WRITE_BOUND_ERR | \ + MALIGP2_REG_VAL_IRQ_SYNC_ERROR | \ + MALIGP2_REG_VAL_IRQ_AXI_BUS_ERROR | \ + MALI400GP_REG_VAL_IRQ_VS_INVALID_CMD | \ + MALI400GP_REG_VAL_IRQ_PLB_INVALID_CMD | \ + MALI400GP_REG_VAL_IRQ_SEMAPHORE_UNDERFLOW | \ + MALI400GP_REG_VAL_IRQ_SEMAPHORE_OVERFLOW | \ + MALI400GP_REG_VAL_IRQ_PTR_ARRAY_OUT_OF_BOUNDS) + +/* Mask defining non IRQs on MaliGP2*/ +#define MALIGP2_REG_VAL_IRQ_MASK_NONE 0 + +/** }@ defgroup MALIGP2_IRQ*/ + +/** @defgroup MALIGP2_STATUS + * The different Status values to the geometry processor. + * @see MALIGP2_CTRL_REG_STATUS + * @{ + */ +#define MALIGP2_REG_VAL_STATUS_VS_ACTIVE 0x0002 +#define MALIGP2_REG_VAL_STATUS_BUS_STOPPED 0x0004 +#define MALIGP2_REG_VAL_STATUS_PLBU_ACTIVE 0x0008 +#define MALIGP2_REG_VAL_STATUS_BUS_ERROR 0x0040 +#define MALIGP2_REG_VAL_STATUS_WRITE_BOUND_ERR 0x0100 +/** }@ defgroup MALIGP2_STATUS*/ + +#define MALIGP2_REG_VAL_STATUS_MASK_ACTIVE (\ + MALIGP2_REG_VAL_STATUS_VS_ACTIVE|\ + MALIGP2_REG_VAL_STATUS_PLBU_ACTIVE) + + +#define MALIGP2_REG_VAL_STATUS_MASK_ERROR (\ + MALIGP2_REG_VAL_STATUS_BUS_ERROR |\ + MALIGP2_REG_VAL_STATUS_WRITE_BOUND_ERR ) + +/* This should be in the top 16 bit of the version register of gp.*/ +#define MALI200_GP_PRODUCT_ID 0xA07 +#define MALI300_GP_PRODUCT_ID 0xC07 +#define MALI400_GP_PRODUCT_ID 0xB07 +#define MALI450_GP_PRODUCT_ID 0xD07 + +/** + * The different sources for instrumented on the geometry processor. + * @see MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_SRC + */ + +enum MALIGP2_cont_reg_perf_cnt_src { + MALIGP2_REG_VAL_PERF_CNT1_SRC_NUMBER_OF_VERTICES_PROCESSED = 0x0a, +}; + +#endif diff --git a/drivers/gpu/arm/mali/timestamp-arm11-cc/mali_timestamp.c b/drivers/gpu/arm/mali/timestamp-arm11-cc/mali_timestamp.c new file mode 100644 index 00000000000000..86c8e31fe485f7 --- /dev/null +++ b/drivers/gpu/arm/mali/timestamp-arm11-cc/mali_timestamp.c @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2010-2011, 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_timestamp.h" + +/* This file is intentionally left empty, as all functions are inlined in mali_profiling_sampler.h */ diff --git a/drivers/gpu/arm/mali/timestamp-arm11-cc/mali_timestamp.h b/drivers/gpu/arm/mali/timestamp-arm11-cc/mali_timestamp.h new file mode 100644 index 00000000000000..175205be3c9185 --- /dev/null +++ b/drivers/gpu/arm/mali/timestamp-arm11-cc/mali_timestamp.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010-2011, 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_TIMESTAMP_H__ +#define __MALI_TIMESTAMP_H__ + +#include "mali_osk.h" + +MALI_STATIC_INLINE _mali_osk_errcode_t _mali_timestamp_reset(void) +{ + /* + * reset counters and overflow flags + */ + + u32 mask = (1 << 0) | /* enable all three counters */ + (0 << 1) | /* reset both Count Registers to 0x0 */ + (1 << 2) | /* reset the Cycle Counter Register to 0x0 */ + (0 << 3) | /* 1 = Cycle Counter Register counts every 64th processor clock cycle */ + (0 << 4) | /* Count Register 0 interrupt enable */ + (0 << 5) | /* Count Register 1 interrupt enable */ + (0 << 6) | /* Cycle Counter interrupt enable */ + (0 << 8) | /* Count Register 0 overflow flag (clear or write, flag on read) */ + (0 << 9) | /* Count Register 1 overflow flag (clear or write, flag on read) */ + (1 << 10); /* Cycle Counter Register overflow flag (clear or write, flag on read) */ + + __asm__ __volatile__ ("MCR p15, 0, %0, c15, c12, 0" : : "r" (mask) ); + + return _MALI_OSK_ERR_OK; +} + +MALI_STATIC_INLINE u64 _mali_timestamp_get(void) +{ + u32 result; + + /* this is for the clock cycles */ + __asm__ __volatile__ ("MRC p15, 0, %0, c15, c12, 1" : "=r" (result)); + + return (u64)result; +} + +#endif /* __MALI_TIMESTAMP_H__ */ diff --git a/drivers/gpu/arm/mali/timestamp-default/mali_timestamp.c b/drivers/gpu/arm/mali/timestamp-default/mali_timestamp.c new file mode 100644 index 00000000000000..86c8e31fe485f7 --- /dev/null +++ b/drivers/gpu/arm/mali/timestamp-default/mali_timestamp.c @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2010-2011, 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_timestamp.h" + +/* This file is intentionally left empty, as all functions are inlined in mali_profiling_sampler.h */ diff --git a/drivers/gpu/arm/mali/timestamp-default/mali_timestamp.h b/drivers/gpu/arm/mali/timestamp-default/mali_timestamp.h new file mode 100644 index 00000000000000..c3a3e1c975a7a8 --- /dev/null +++ b/drivers/gpu/arm/mali/timestamp-default/mali_timestamp.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2010-2011, 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_TIMESTAMP_H__ +#define __MALI_TIMESTAMP_H__ + +#include "mali_osk.h" + +MALI_STATIC_INLINE _mali_osk_errcode_t _mali_timestamp_reset(void) +{ + return _MALI_OSK_ERR_OK; +} + +MALI_STATIC_INLINE u64 _mali_timestamp_get(void) +{ + return _mali_osk_time_get_ns(); +} + +#endif /* __MALI_TIMESTAMP_H__ */ diff --git a/drivers/gpu/arm/ump/Kbuild b/drivers/gpu/arm/ump/Kbuild new file mode 100644 index 00000000000000..4f62b785317787 --- /dev/null +++ b/drivers/gpu/arm/ump/Kbuild @@ -0,0 +1,94 @@ +# +# Copyright (C) 2010-2012 ARM Limited. All rights reserved. +# +# This program is free software and is provided to you under the terms of the GNU General Public License version 2 +# as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. +# +# A copy of the licence is included with the program, and can also be obtained from Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# + +# Set default configuration to use, if Makefile didn't provide one. +# Change this to use a different config.h +CONFIG ?= os_memory_64m + +# Validate selected config +ifneq ($(shell [ -d $(src)/arch-$(CONFIG) ] && [ -f $(src)/arch-$(CONFIG)/config.h ] && echo "OK"), OK) +$(warning Current directory is $(src)) +$(error No configuration found for config $(CONFIG). Check that arch-$(CONFIG)/config.h exists) +else +# Link arch to the selected arch-config directory +$(shell [ -L $(src)/arch ] && rm $(src)/arch) +$(shell ln -sf arch-$(CONFIG) $(src)/arch) +$(shell touch $(src)/arch/config.h) +endif + +UDD_FILE_PREFIX = ../mali/ + +# Get subversion revision number, fall back to 0000 if no svn info is available +SVN_INFO = (cd $(src); svn info 2>/dev/null) + +ifneq ($(shell $(SVN_INFO) 2>/dev/null),) +# SVN detected +SVN_REV := $(shell $(SVN_INFO) | grep '^Revision: '| sed -e 's/^Revision: //' 2>/dev/null) +DRIVER_REV := $(MALI_RELEASE_NAME)-r$(SVN_REV) +CHANGE_DATE := $(shell $(SVN_INFO) | grep '^Last Changed Date: ' | cut -d: -f2- | cut -b2-) +CHANGED_REVISION := $(shell $(SVN_INFO) | grep '^Last Changed Rev: ' | cut -d: -f2- | cut -b2-) +REPO_URL := $(shell $(SVN_INFO) | grep '^URL: ' | cut -d: -f2- | cut -b2-) + +else # SVN +GIT_REV := $(shell cd $(src); git describe --always 2>/dev/null) +ifneq ($(GIT_REV),) +# Git detected +DRIVER_REV := $(MALI_RELEASE_NAME)-$(GIT_REV) +CHANGE_DATE := $(shell cd $(src); git log -1 --format="%ci") +CHANGED_REVISION := $(GIT_REV) +REPO_URL := $(shell cd $(src); git describe --all --always 2>/dev/null) + +else # Git +# No Git or SVN detected +DRIVER_REV := $(MALI_RELEASE_NAME) +CHANGE_DATE := $(MALI_RELEASE_NAME) +CHANGED_REVISION := $(MALI_RELEASE_NAME) +endif +endif + +ccflags-y += -DSVN_REV=$(SVN_REV) +ccflags-y += -DSVN_REV_STRING=\"$(DRIVER_REV)\" + +ccflags-y += -I$(src) -I$(src)/common -I$(src)/linux -I$(src)/../mali/common -I$(src)/../mali/linux -I$(src)/../../ump/include/ump +ccflags-y += -DMALI_STATE_TRACKING=0 +ccflags-y += -DMALI_ENABLE_CPU_CYCLES=0 +ccflags-$(CONFIG_UMP_DEBUG) += -DDEBUG + +# For customer releases the Linux Device Drivers will be provided as ARM proprietary and GPL releases: +# The ARM proprietary product will only include the license/proprietary directory +# The GPL product will only include the license/gpl directory + +ifeq ($(wildcard $(src)/linux/license/gpl/*),) +ccflags-y += -I$(src)/linux/license/proprietary -I$(src)/../mali/linux/license/proprietary +else +ccflags-y += -I$(src)/linux/license/gpl -I$(src)/../mali/linux/license/gpl +endif + +ump-y = common/ump_kernel_common.o \ + common/ump_kernel_descriptor_mapping.o \ + common/ump_kernel_api.o \ + common/ump_kernel_ref_drv.o \ + linux/ump_kernel_linux.o \ + linux/ump_kernel_memory_backend_os.o \ + linux/ump_kernel_memory_backend_dedicated.o \ + linux/ump_memory_backend.o \ + linux/ump_ukk_wrappers.o \ + linux/ump_ukk_ref_wrappers.o \ + linux/ump_osk_atomics.o \ + linux/ump_osk_low_level_mem.o \ + linux/ump_osk_misc.o \ + $(UDD_FILE_PREFIX)linux/mali_osk_atomics.o \ + $(UDD_FILE_PREFIX)linux/mali_osk_locks.o \ + $(UDD_FILE_PREFIX)linux/mali_osk_memory.o \ + $(UDD_FILE_PREFIX)linux/mali_osk_math.o \ + $(UDD_FILE_PREFIX)linux/mali_osk_misc.o + +obj-$(CONFIG_UMP) := ump.o + diff --git a/drivers/gpu/arm/ump/Kconfig b/drivers/gpu/arm/ump/Kconfig new file mode 100644 index 00000000000000..3ae316c90ca3e8 --- /dev/null +++ b/drivers/gpu/arm/ump/Kconfig @@ -0,0 +1,16 @@ +config UMP + tristate "UMP support" + depends on ARM + ---help--- + This enables support for the UMP memory allocation and sharing API. + + To compile this driver as a module, choose M here: the module will be + called ump. + +config UMP_DEBUG + bool "Enable extra debug in UMP" + depends on UMP + default y + ---help--- + This enabled extra debug checks and messages in UMP. + diff --git a/drivers/gpu/arm/ump/Makefile b/drivers/gpu/arm/ump/Makefile new file mode 100644 index 00000000000000..e2aa8e5cd1c551 --- /dev/null +++ b/drivers/gpu/arm/ump/Makefile @@ -0,0 +1,67 @@ +# +# Copyright (C) 2010-2012 ARM Limited. All rights reserved. +# +# This program is free software and is provided to you under the terms of the GNU General Public License version 2 +# as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. +# +# A copy of the licence is included with the program, and can also be obtained from Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# + +# For each arch check: CROSS_COMPILE , KDIR , CFLAGS += -DARCH + +export ARCH ?= arm +BUILD ?= debug + +check_cc2 = \ + $(shell if $(1) -S -o /dev/null -xc /dev/null > /dev/null 2>&1; \ + then \ + echo "$(2)"; \ + else \ + echo "$(3)"; \ + fi ;) + +# Check that required parameters are supplied. +ifeq ($(CONFIG),) +$(error "CONFIG must be specified.") +endif +ifeq ($(CPU)$(KDIR),) +$(error "KDIR or CPU must be specified.") +endif + +# Get any user defined KDIR- or maybe even a hardcoded KDIR +-include KDIR_CONFIGURATION + +# Define host system directory +KDIR-$(shell uname -m):=/lib/modules/$(shell uname -r)/build + +ifeq ($(ARCH), arm) +# when compiling for ARM we're cross compiling +export CROSS_COMPILE ?= $(call check_cc2, arm-linux-gnueabi-gcc, arm-linux-gnueabi-, arm-none-linux-gnueabi-) +endif + +# look up KDIR based om CPU selection +KDIR ?= $(KDIR-$(CPU)) + +export CONFIG + +export CONFIG_UMP := m +ifeq ($(BUILD),debug) +export CONFIG_UMP_DEBUG := y +else +export CONFIG_UMP_DEBUG := n +endif + +ifeq ($(KDIR),) +$(error No KDIR found for platform $(CPU)) +endif + +all: + $(MAKE) -C $(KDIR) M=$(CURDIR) modules + +kernelrelease: + $(MAKE) -C $(KDIR) kernelrelease + +clean: + $(MAKE) -C $(KDIR) M=$(CURDIR) clean + $(MAKE) -C $(KDIR) M=$(CURDIR)/../mali clean diff --git a/drivers/gpu/arm/ump/Makefile.common b/drivers/gpu/arm/ump/Makefile.common new file mode 100644 index 00000000000000..e750ed7d5e3aaf --- /dev/null +++ b/drivers/gpu/arm/ump/Makefile.common @@ -0,0 +1,20 @@ +# +# Copyright (C) 2010-2011, 2013 ARM Limited. All rights reserved. +# +# This program is free software and is provided to you under the terms of the GNU General Public License version 2 +# as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. +# +# A copy of the licence is included with the program, and can also be obtained from Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# + +SRC = $(UMP_FILE_PREFIX)common/ump_kernel_common.c \ + $(UMP_FILE_PREFIX)common/ump_kernel_descriptor_mapping.c \ + $(UMP_FILE_PREFIX)common/ump_kernel_api.c \ + $(UMP_FILE_PREFIX)common/ump_kernel_ref_drv.c + +# Get subversion revision number, fall back to 0000 if no svn info is available +SVN_REV:=$(shell ((svnversion | grep -qv exported && echo -n 'Revision: ' && svnversion) || git svn info | sed -e 's/$$$$/M/' | grep '^Revision: ' || echo ${MALI_RELEASE_NAME}) 2>/dev/null | sed -e 's/^Revision: //') + +EXTRA_CFLAGS += -DSVN_REV=$(SVN_REV) +EXTRA_CFLAGS += -DSVN_REV_STRING=\"$(SVN_REV)\" diff --git a/drivers/gpu/arm/ump/arch-pb-virtex5/config.h b/drivers/gpu/arm/ump/arch-pb-virtex5/config.h new file mode 100644 index 00000000000000..850e28d5e99f35 --- /dev/null +++ b/drivers/gpu/arm/ump/arch-pb-virtex5/config.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ARCH_CONFIG_H__ +#define __ARCH_CONFIG_H__ + +#define ARCH_UMP_BACKEND_DEFAULT 0 +#define ARCH_UMP_MEMORY_ADDRESS_DEFAULT 0xE1000000 +#define ARCH_UMP_MEMORY_SIZE_DEFAULT 16UL * 1024UL * 1024UL + +#endif /* __ARCH_CONFIG_H__ */ diff --git a/drivers/gpu/arm/ump/common/ump_kernel_api.c b/drivers/gpu/arm/ump/common/ump_kernel_api.c new file mode 100644 index 00000000000000..000a91287164be --- /dev/null +++ b/drivers/gpu/arm/ump/common/ump_kernel_api.c @@ -0,0 +1,492 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_osk.h" +#include "mali_osk_list.h" +#include "ump_osk.h" +#include "ump_uk_types.h" +#include "ump_kernel_interface.h" +#include "ump_kernel_common.h" + + + +/* ---------------- UMP kernel space API functions follows ---------------- */ + + + +UMP_KERNEL_API_EXPORT ump_secure_id ump_dd_secure_id_get(ump_dd_handle memh) +{ + ump_dd_mem * mem = (ump_dd_mem *)memh; + + DEBUG_ASSERT_POINTER(mem); + + DBG_MSG(5, ("Returning secure ID. ID: %u\n", mem->secure_id)); + + return mem->secure_id; +} + + + +UMP_KERNEL_API_EXPORT ump_dd_handle ump_dd_handle_create_from_secure_id(ump_secure_id secure_id) +{ + ump_dd_mem * mem; + + _mali_osk_mutex_wait(device.secure_id_map_lock); + + DBG_MSG(5, ("Getting handle from secure ID. ID: %u\n", secure_id)); + if (0 != ump_descriptor_mapping_get(device.secure_id_map, (int)secure_id, (void**)&mem)) { + _mali_osk_mutex_signal(device.secure_id_map_lock); + DBG_MSG(1, ("Secure ID not found. ID: %u\n", secure_id)); + return UMP_DD_HANDLE_INVALID; + } + + ump_dd_reference_add(mem); + + _mali_osk_mutex_signal(device.secure_id_map_lock); + + return (ump_dd_handle)mem; +} + + + +UMP_KERNEL_API_EXPORT unsigned long ump_dd_phys_block_count_get(ump_dd_handle memh) +{ + ump_dd_mem * mem = (ump_dd_mem*) memh; + + DEBUG_ASSERT_POINTER(mem); + + return mem->nr_blocks; +} + + + +UMP_KERNEL_API_EXPORT ump_dd_status_code ump_dd_phys_blocks_get(ump_dd_handle memh, ump_dd_physical_block * blocks, unsigned long num_blocks) +{ + ump_dd_mem * mem = (ump_dd_mem *)memh; + + DEBUG_ASSERT_POINTER(mem); + + if (blocks == NULL) { + DBG_MSG(1, ("NULL parameter in ump_dd_phys_blocks_get()\n")); + return UMP_DD_INVALID; + } + + if (mem->nr_blocks != num_blocks) { + DBG_MSG(1, ("Specified number of blocks do not match actual number of blocks\n")); + return UMP_DD_INVALID; + } + + DBG_MSG(5, ("Returning physical block information. ID: %u\n", mem->secure_id)); + + _mali_osk_memcpy(blocks, mem->block_array, sizeof(ump_dd_physical_block) * mem->nr_blocks); + + return UMP_DD_SUCCESS; +} + + + +UMP_KERNEL_API_EXPORT ump_dd_status_code ump_dd_phys_block_get(ump_dd_handle memh, unsigned long index, ump_dd_physical_block * block) +{ + ump_dd_mem * mem = (ump_dd_mem *)memh; + + DEBUG_ASSERT_POINTER(mem); + + if (block == NULL) { + DBG_MSG(1, ("NULL parameter in ump_dd_phys_block_get()\n")); + return UMP_DD_INVALID; + } + + if (index >= mem->nr_blocks) { + DBG_MSG(5, ("Invalid index specified in ump_dd_phys_block_get()\n")); + return UMP_DD_INVALID; + } + + DBG_MSG(5, ("Returning physical block information. ID: %u, index: %lu\n", mem->secure_id, index)); + + *block = mem->block_array[index]; + + return UMP_DD_SUCCESS; +} + + + +UMP_KERNEL_API_EXPORT unsigned long ump_dd_size_get(ump_dd_handle memh) +{ + ump_dd_mem * mem = (ump_dd_mem*)memh; + + DEBUG_ASSERT_POINTER(mem); + + DBG_MSG(5, ("Returning size. ID: %u, size: %lu\n", mem->secure_id, mem->size_bytes)); + + return mem->size_bytes; +} + + + +UMP_KERNEL_API_EXPORT void ump_dd_reference_add(ump_dd_handle memh) +{ + ump_dd_mem * mem = (ump_dd_mem*)memh; + int new_ref; + + DEBUG_ASSERT_POINTER(mem); + + new_ref = _ump_osk_atomic_inc_and_read(&mem->ref_count); + + DBG_MSG(5, ("Memory reference incremented. ID: %u, new value: %d\n", mem->secure_id, new_ref)); +} + + + +UMP_KERNEL_API_EXPORT void ump_dd_reference_release(ump_dd_handle memh) +{ + int new_ref; + ump_dd_mem * mem = (ump_dd_mem*)memh; + + DEBUG_ASSERT_POINTER(mem); + + /* We must hold this mutex while doing the atomic_dec_and_read, to protect + that elements in the ump_descriptor_mapping table is always valid. If they + are not, userspace may accidently map in this secure_ids right before its freed + giving a mapped backdoor into unallocated memory.*/ + _mali_osk_mutex_wait(device.secure_id_map_lock); + + new_ref = _ump_osk_atomic_dec_and_read(&mem->ref_count); + + DBG_MSG(5, ("Memory reference decremented. ID: %u, new value: %d\n", mem->secure_id, new_ref)); + + if (0 == new_ref) { + DBG_MSG(3, ("Final release of memory. ID: %u\n", mem->secure_id)); + + ump_descriptor_mapping_free(device.secure_id_map, (int)mem->secure_id); + + _mali_osk_mutex_signal(device.secure_id_map_lock); + mem->release_func(mem->ctx, mem); + _mali_osk_free(mem); + } else { + _mali_osk_mutex_signal(device.secure_id_map_lock); + } +} + + + +/* --------------- Handling of user space requests follows --------------- */ + + +_mali_osk_errcode_t _ump_uku_get_api_version( _ump_uk_api_version_s *args ) +{ + ump_session_data * session_data; + + DEBUG_ASSERT_POINTER( args ); + DEBUG_ASSERT_POINTER( args->ctx ); + + session_data = (ump_session_data *)args->ctx; + + /* check compatability */ + if (args->version == UMP_IOCTL_API_VERSION) { + DBG_MSG(3, ("API version set to newest %d (compatible)\n", GET_VERSION(args->version))); + args->compatible = 1; + session_data->api_version = args->version; + } else if (args->version == MAKE_VERSION_ID(1)) { + DBG_MSG(2, ("API version set to depricated: %d (compatible)\n", GET_VERSION(args->version))); + args->compatible = 1; + session_data->api_version = args->version; + } else { + DBG_MSG(2, ("API version set to %d (incompatible with client version %d)\n", GET_VERSION(UMP_IOCTL_API_VERSION), GET_VERSION(args->version))); + args->compatible = 0; + args->version = UMP_IOCTL_API_VERSION; /* report our version */ + } + + return _MALI_OSK_ERR_OK; +} + + +_mali_osk_errcode_t _ump_ukk_release( _ump_uk_release_s *release_info ) +{ + ump_session_memory_list_element * session_memory_element; + ump_session_memory_list_element * tmp; + ump_session_data * session_data; + _mali_osk_errcode_t ret = _MALI_OSK_ERR_INVALID_FUNC; + int secure_id; + + DEBUG_ASSERT_POINTER( release_info ); + DEBUG_ASSERT_POINTER( release_info->ctx ); + + /* Retreive the session data */ + session_data = (ump_session_data*)release_info->ctx; + + /* If there are many items in the memory session list we + * could be de-referencing this pointer a lot so keep a local copy + */ + secure_id = release_info->secure_id; + + DBG_MSG(4, ("Releasing memory with IOCTL, ID: %u\n", secure_id)); + + /* Iterate through the memory list looking for the requested secure ID */ + _mali_osk_mutex_wait(session_data->lock); + _MALI_OSK_LIST_FOREACHENTRY(session_memory_element, tmp, &session_data->list_head_session_memory_list, ump_session_memory_list_element, list) { + if ( session_memory_element->mem->secure_id == secure_id) { + ump_dd_mem *release_mem; + + release_mem = session_memory_element->mem; + _mali_osk_list_del(&session_memory_element->list); + ump_dd_reference_release(release_mem); + _mali_osk_free(session_memory_element); + + ret = _MALI_OSK_ERR_OK; + break; + } + } + + _mali_osk_mutex_signal(session_data->lock); + DBG_MSG_IF(1, _MALI_OSK_ERR_OK != ret, ("UMP memory with ID %u does not belong to this session.\n", secure_id)); + + DBG_MSG(4, ("_ump_ukk_release() returning 0x%x\n", ret)); + return ret; +} + +_mali_osk_errcode_t _ump_ukk_size_get( _ump_uk_size_get_s *user_interaction ) +{ + ump_dd_mem * mem; + _mali_osk_errcode_t ret = _MALI_OSK_ERR_FAULT; + + DEBUG_ASSERT_POINTER( user_interaction ); + + /* We lock the mappings so things don't get removed while we are looking for the memory */ + _mali_osk_mutex_wait(device.secure_id_map_lock); + if (0 == ump_descriptor_mapping_get(device.secure_id_map, (int)user_interaction->secure_id, (void**)&mem)) { + user_interaction->size = mem->size_bytes; + DBG_MSG(4, ("Returning size. ID: %u, size: %lu ", (ump_secure_id)user_interaction->secure_id, (unsigned long)user_interaction->size)); + ret = _MALI_OSK_ERR_OK; + } else { + user_interaction->size = 0; + DBG_MSG(1, ("Failed to look up mapping in ump_ioctl_size_get(). ID: %u\n", (ump_secure_id)user_interaction->secure_id)); + } + + _mali_osk_mutex_signal(device.secure_id_map_lock); + return ret; +} + + + +void _ump_ukk_msync( _ump_uk_msync_s *args ) +{ + ump_dd_mem * mem = NULL; + void *virtual = NULL; + u32 size = 0; + u32 offset = 0; + + _mali_osk_mutex_wait(device.secure_id_map_lock); + ump_descriptor_mapping_get(device.secure_id_map, (int)args->secure_id, (void**)&mem); + + if (NULL == mem) { + _mali_osk_mutex_signal(device.secure_id_map_lock); + DBG_MSG(1, ("Failed to look up mapping in _ump_ukk_msync(). ID: %u\n", (ump_secure_id)args->secure_id)); + return; + } + /* Ensure the memory doesn't dissapear when we are flushing it. */ + ump_dd_reference_add(mem); + _mali_osk_mutex_signal(device.secure_id_map_lock); + + /* Returns the cache settings back to Userspace */ + args->is_cached=mem->is_cached; + + /* If this flag is the only one set, we should not do the actual flush, only the readout */ + if ( _UMP_UK_MSYNC_READOUT_CACHE_ENABLED==args->op ) { + DBG_MSG(3, ("_ump_ukk_msync READOUT ID: %u Enabled: %d\n", (ump_secure_id)args->secure_id, mem->is_cached)); + goto msync_release_and_return; + } + + /* Nothing to do if the memory is not caches */ + if ( 0==mem->is_cached ) { + DBG_MSG(3, ("_ump_ukk_msync IGNORING ID: %u Enabled: %d OP: %d\n", (ump_secure_id)args->secure_id, mem->is_cached, args->op)); + goto msync_release_and_return; + } + DBG_MSG(3, ("UMP[%02u] _ump_ukk_msync Flush OP: %d Address: 0x%08x Mapping: 0x%08x\n", + (ump_secure_id)args->secure_id, args->op, args->address, args->mapping)); + + if ( args->address ) { + virtual = (void *)((u32)args->address); + offset = (u32)((args->address) - (args->mapping)); + } else { + /* Flush entire mapping when no address is specified. */ + virtual = args->mapping; + } + if ( args->size ) { + size = args->size; + } else { + /* Flush entire mapping when no size is specified. */ + size = mem->size_bytes - offset; + } + + if ( (offset + size) > mem->size_bytes ) { + DBG_MSG(1, ("Trying to flush more than the entire UMP allocation: offset: %u + size: %u > %u\n", offset, size, mem->size_bytes)); + goto msync_release_and_return; + } + + /* The actual cache flush - Implemented for each OS*/ + _ump_osk_msync( mem, virtual, offset, size, args->op, NULL); + +msync_release_and_return: + ump_dd_reference_release(mem); + return; +} + +void _ump_ukk_cache_operations_control(_ump_uk_cache_operations_control_s* args) +{ + ump_session_data * session_data; + ump_uk_cache_op_control op; + + DEBUG_ASSERT_POINTER( args ); + DEBUG_ASSERT_POINTER( args->ctx ); + + op = args->op; + session_data = (ump_session_data *)args->ctx; + + _mali_osk_mutex_wait(session_data->lock); + if ( op== _UMP_UK_CACHE_OP_START ) { + session_data->cache_operations_ongoing++; + DBG_MSG(4, ("Cache ops start\n" )); + if ( session_data->cache_operations_ongoing != 1 ) { + DBG_MSG(2, ("UMP: Number of simultanious cache control ops: %d\n", session_data->cache_operations_ongoing) ); + } + } else if ( op== _UMP_UK_CACHE_OP_FINISH ) { + DBG_MSG(4, ("Cache ops finish\n")); + session_data->cache_operations_ongoing--; +#if 0 + if ( session_data->has_pending_level1_cache_flush) { + /* This function will set has_pending_level1_cache_flush=0 */ + _ump_osk_msync( NULL, NULL, 0, 0, _UMP_UK_MSYNC_FLUSH_L1, session_data); + } +#endif + + /* to be on the safe side: always flush l1 cache when cache operations are done */ + _ump_osk_msync( NULL, NULL, 0, 0, _UMP_UK_MSYNC_FLUSH_L1, session_data); + DBG_MSG(4, ("Cache ops finish end\n" )); + } else { + DBG_MSG(1, ("Illegal call to %s at line %d\n", __FUNCTION__, __LINE__)); + } + _mali_osk_mutex_signal(session_data->lock); + +} + +void _ump_ukk_switch_hw_usage(_ump_uk_switch_hw_usage_s *args ) +{ + ump_dd_mem * mem = NULL; + ump_uk_user old_user; + ump_uk_msync_op cache_op = _UMP_UK_MSYNC_CLEAN_AND_INVALIDATE; + ump_session_data *session_data; + + DEBUG_ASSERT_POINTER( args ); + DEBUG_ASSERT_POINTER( args->ctx ); + + session_data = (ump_session_data *)args->ctx; + + _mali_osk_mutex_wait(device.secure_id_map_lock); + ump_descriptor_mapping_get(device.secure_id_map, (int)args->secure_id, (void**)&mem); + + if (NULL == mem) { + _mali_osk_mutex_signal(device.secure_id_map_lock); + DBG_MSG(1, ("Failed to look up mapping in _ump_ukk_switch_hw_usage(). ID: %u\n", (ump_secure_id)args->secure_id)); + return; + } + + old_user = mem->hw_device; + mem->hw_device = args->new_user; + + DBG_MSG(3, ("UMP[%02u] Switch usage Start New: %s Prev: %s.\n", (ump_secure_id)args->secure_id, args->new_user?"MALI":"CPU",old_user?"MALI":"CPU")); + + if ( ! mem->is_cached ) { + _mali_osk_mutex_signal(device.secure_id_map_lock); + DBG_MSG(3, ("UMP[%02u] Changing owner of uncached memory. Cache flushing not needed.\n", (ump_secure_id)args->secure_id)); + return; + } + + if ( old_user == args->new_user) { + _mali_osk_mutex_signal(device.secure_id_map_lock); + DBG_MSG(4, ("UMP[%02u] Setting the new_user equal to previous for. Cache flushing not needed.\n", (ump_secure_id)args->secure_id)); + return; + } + if ( + /* Previous AND new is both different from CPU */ + (old_user != _UMP_UK_USED_BY_CPU) && (args->new_user != _UMP_UK_USED_BY_CPU ) + ) { + _mali_osk_mutex_signal(device.secure_id_map_lock); + DBG_MSG(4, ("UMP[%02u] Previous and new user is not CPU. Cache flushing not needed.\n", (ump_secure_id)args->secure_id)); + return; + } + + if ( (old_user != _UMP_UK_USED_BY_CPU ) && (args->new_user==_UMP_UK_USED_BY_CPU) ) { + cache_op =_UMP_UK_MSYNC_INVALIDATE; + DBG_MSG(4, ("UMP[%02u] Cache invalidation needed\n", (ump_secure_id)args->secure_id)); +#ifdef UMP_SKIP_INVALIDATION +#error + _mali_osk_mutex_signal(device.secure_id_map_lock); + DBG_MSG(4, ("UMP[%02u] Performing Cache invalidation SKIPPED\n", (ump_secure_id)args->secure_id)); + return; +#endif + } + /* Ensure the memory doesn't dissapear when we are flushing it. */ + ump_dd_reference_add(mem); + _mali_osk_mutex_signal(device.secure_id_map_lock); + + /* Take lock to protect: session->cache_operations_ongoing and session->has_pending_level1_cache_flush */ + _mali_osk_mutex_wait(session_data->lock); + /* Actual cache flush */ + _ump_osk_msync( mem, NULL, 0, mem->size_bytes, cache_op, session_data); + _mali_osk_mutex_signal(session_data->lock); + + ump_dd_reference_release(mem); + DBG_MSG(4, ("UMP[%02u] Switch usage Finish\n", (ump_secure_id)args->secure_id)); + return; +} + +void _ump_ukk_lock(_ump_uk_lock_s *args ) +{ + ump_dd_mem * mem = NULL; + + _mali_osk_mutex_wait(device.secure_id_map_lock); + ump_descriptor_mapping_get(device.secure_id_map, (int)args->secure_id, (void**)&mem); + + if (NULL == mem) { + _mali_osk_mutex_signal(device.secure_id_map_lock); + DBG_MSG(1, ("UMP[%02u] Failed to look up mapping in _ump_ukk_lock(). ID: %u\n", (ump_secure_id)args->secure_id)); + return; + } + ump_dd_reference_add(mem); + _mali_osk_mutex_signal(device.secure_id_map_lock); + + DBG_MSG(1, ("UMP[%02u] Lock. New lock flag: %d. Old Lock flag:\n", (u32)args->secure_id, (u32)args->lock_usage, (u32) mem->lock_usage )); + + mem->lock_usage = (ump_lock_usage) args->lock_usage; + + ump_dd_reference_release(mem); +} + +void _ump_ukk_unlock(_ump_uk_unlock_s *args ) +{ + ump_dd_mem * mem = NULL; + + _mali_osk_mutex_wait(device.secure_id_map_lock); + ump_descriptor_mapping_get(device.secure_id_map, (int)args->secure_id, (void**)&mem); + + if (NULL == mem) { + _mali_osk_mutex_signal(device.secure_id_map_lock); + DBG_MSG(1, ("Failed to look up mapping in _ump_ukk_unlock(). ID: %u\n", (ump_secure_id)args->secure_id)); + return; + } + ump_dd_reference_add(mem); + _mali_osk_mutex_signal(device.secure_id_map_lock); + + DBG_MSG(1, ("UMP[%02u] Unlocking. Old Lock flag:\n", (u32)args->secure_id, (u32) mem->lock_usage )); + + mem->lock_usage = (ump_lock_usage) UMP_NOT_LOCKED; + + ump_dd_reference_release(mem); +} diff --git a/drivers/gpu/arm/ump/common/ump_kernel_common.c b/drivers/gpu/arm/ump/common/ump_kernel_common.c new file mode 100644 index 00000000000000..e207eea0c59644 --- /dev/null +++ b/drivers/gpu/arm/ump/common/ump_kernel_common.c @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_osk_bitops.h" +#include "mali_osk_list.h" +#include "ump_osk.h" +#include "ump_uk_types.h" +#include "ump_ukk.h" +#include "ump_kernel_common.h" +#include "ump_kernel_descriptor_mapping.h" +#include "ump_kernel_memory_backend.h" + + + +/** + * Define the initial and maximum size of number of secure_ids on the system + */ +#define UMP_SECURE_ID_TABLE_ENTRIES_INITIAL (128 ) +#define UMP_SECURE_ID_TABLE_ENTRIES_MAXIMUM (4096 ) + + +/** + * Define the initial and maximum size of the ump_session_data::cookies_map, + * which is a \ref ump_descriptor_mapping. This limits how many secure_ids + * may be mapped into a particular process using _ump_ukk_map_mem(). + */ + +#define UMP_COOKIES_PER_SESSION_INITIAL (UMP_SECURE_ID_TABLE_ENTRIES_INITIAL ) +#define UMP_COOKIES_PER_SESSION_MAXIMUM (UMP_SECURE_ID_TABLE_ENTRIES_MAXIMUM) + +struct ump_dev device; + +_mali_osk_errcode_t ump_kernel_constructor(void) +{ + _mali_osk_errcode_t err; + + /* Perform OS Specific initialization */ + err = _ump_osk_init(); + if( _MALI_OSK_ERR_OK != err ) { + MSG_ERR(("Failed to initiaze the UMP Device Driver")); + return err; + } + + /* Init the global device */ + _mali_osk_memset(&device, 0, sizeof(device) ); + + /* Create the descriptor map, which will be used for mapping secure ID to ump_dd_mem structs */ + device.secure_id_map_lock = _mali_osk_mutex_init(_MALI_OSK_LOCKFLAG_UNORDERED, 0); + if (NULL == device.secure_id_map_lock) { + MSG_ERR(("Failed to create OSK lock for secure id lookup table\n")); + return _MALI_OSK_ERR_NOMEM; + } + + device.secure_id_map = ump_descriptor_mapping_create(UMP_SECURE_ID_TABLE_ENTRIES_INITIAL, UMP_SECURE_ID_TABLE_ENTRIES_MAXIMUM); + if (NULL == device.secure_id_map) { + _mali_osk_mutex_term(device.secure_id_map_lock); + MSG_ERR(("Failed to create secure id lookup table\n")); + return _MALI_OSK_ERR_NOMEM; + } + + /* Init memory backend */ + device.backend = ump_memory_backend_create(); + if (NULL == device.backend) { + MSG_ERR(("Failed to create memory backend\n")); + _mali_osk_mutex_term(device.secure_id_map_lock); + ump_descriptor_mapping_destroy(device.secure_id_map); + return _MALI_OSK_ERR_NOMEM; + } + + return _MALI_OSK_ERR_OK; +} + +void ump_kernel_destructor(void) +{ + DEBUG_ASSERT_POINTER(device.secure_id_map); + DEBUG_ASSERT_POINTER(device.secure_id_map_lock); + + _mali_osk_mutex_term(device.secure_id_map_lock); + device.secure_id_map_lock = NULL; + + ump_descriptor_mapping_destroy(device.secure_id_map); + device.secure_id_map = NULL; + + device.backend->shutdown(device.backend); + device.backend = NULL; + + ump_memory_backend_destroy(); + + _ump_osk_term(); +} + +/** Creates a new UMP session + */ +_mali_osk_errcode_t _ump_ukk_open( void** context ) +{ + struct ump_session_data * session_data; + + /* allocated struct to track this session */ + session_data = (struct ump_session_data *)_mali_osk_malloc(sizeof(struct ump_session_data)); + if (NULL == session_data) { + MSG_ERR(("Failed to allocate ump_session_data in ump_file_open()\n")); + return _MALI_OSK_ERR_NOMEM; + } + + session_data->lock = _mali_osk_mutex_init(_MALI_OSK_LOCKFLAG_UNORDERED, 0); + if( NULL == session_data->lock ) { + MSG_ERR(("Failed to initialize lock for ump_session_data in ump_file_open()\n")); + _mali_osk_free(session_data); + return _MALI_OSK_ERR_NOMEM; + } + + session_data->cookies_map = ump_descriptor_mapping_create( UMP_COOKIES_PER_SESSION_INITIAL, UMP_COOKIES_PER_SESSION_MAXIMUM ); + + if ( NULL == session_data->cookies_map ) { + MSG_ERR(("Failed to create descriptor mapping for _ump_ukk_map_mem cookies\n")); + + _mali_osk_mutex_term(session_data->lock); + _mali_osk_free( session_data ); + return _MALI_OSK_ERR_NOMEM; + } + + _MALI_OSK_INIT_LIST_HEAD(&session_data->list_head_session_memory_list); + + _MALI_OSK_INIT_LIST_HEAD(&session_data->list_head_session_memory_mappings_list); + + /* Since initial version of the UMP interface did not use the API_VERSION ioctl we have to assume + that it is this version, and not the "latest" one: UMP_IOCTL_API_VERSION + Current and later API versions would do an additional call to this IOCTL and update this variable + to the correct one.*/ + session_data->api_version = MAKE_VERSION_ID(1); + + *context = (void*)session_data; + + session_data->cache_operations_ongoing = 0 ; + session_data->has_pending_level1_cache_flush = 0; + + DBG_MSG(2, ("New session opened\n")); + + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t _ump_ukk_close( void** context ) +{ + struct ump_session_data * session_data; + ump_session_memory_list_element * item; + ump_session_memory_list_element * tmp; + + session_data = (struct ump_session_data *)*context; + if (NULL == session_data) { + MSG_ERR(("Session data is NULL in _ump_ukk_close()\n")); + return _MALI_OSK_ERR_INVALID_ARGS; + } + + /* Unmap any descriptors mapped in. */ + if (0 == _mali_osk_list_empty(&session_data->list_head_session_memory_mappings_list)) { + ump_memory_allocation *descriptor; + ump_memory_allocation *temp; + + DBG_MSG(1, ("Memory mappings found on session usage list during session termination\n")); + + /* use the 'safe' list iterator, since freeing removes the active block from the list we're iterating */ + _MALI_OSK_LIST_FOREACHENTRY(descriptor, temp, &session_data->list_head_session_memory_mappings_list, ump_memory_allocation, list) { + _ump_uk_unmap_mem_s unmap_args; + DBG_MSG(4, ("Freeing block with phys address 0x%x size 0x%x mapped in user space at 0x%x\n", + descriptor->phys_addr, descriptor->size, descriptor->mapping)); + unmap_args.ctx = (void*)session_data; + unmap_args.mapping = descriptor->mapping; + unmap_args.size = descriptor->size; + unmap_args._ukk_private = NULL; /* NOTE: unused */ + unmap_args.cookie = descriptor->cookie; + + /* NOTE: This modifies the list_head_session_memory_mappings_list */ + _ump_ukk_unmap_mem( &unmap_args ); + } + } + + /* ASSERT that we really did free everything, because _ump_ukk_unmap_mem() + * can fail silently. */ + DEBUG_ASSERT( _mali_osk_list_empty(&session_data->list_head_session_memory_mappings_list) ); + + _MALI_OSK_LIST_FOREACHENTRY(item, tmp, &session_data->list_head_session_memory_list, ump_session_memory_list_element, list) { + _mali_osk_list_del(&item->list); + DBG_MSG(2, ("Releasing UMP memory %u as part of file close\n", item->mem->secure_id)); + ump_dd_reference_release(item->mem); + _mali_osk_free(item); + } + + ump_descriptor_mapping_destroy( session_data->cookies_map ); + + _mali_osk_mutex_term(session_data->lock); + _mali_osk_free(session_data); + + DBG_MSG(2, ("Session closed\n")); + + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t _ump_ukk_map_mem( _ump_uk_map_mem_s *args ) +{ + struct ump_session_data * session_data; + ump_memory_allocation * descriptor; /* Describes current mapping of memory */ + _mali_osk_errcode_t err; + unsigned long offset = 0; + unsigned long left; + ump_dd_handle handle; /* The real UMP handle for this memory. Its real datatype is ump_dd_mem* */ + ump_dd_mem * mem; /* The real UMP memory. It is equal to the handle, but with exposed struct */ + u32 block; + int map_id; + + session_data = (ump_session_data *)args->ctx; + if( NULL == session_data ) { + MSG_ERR(("Session data is NULL in _ump_ukk_map_mem()\n")); + return _MALI_OSK_ERR_INVALID_ARGS; + } + + descriptor = (ump_memory_allocation*) _mali_osk_calloc( 1, sizeof(ump_memory_allocation)); + if (NULL == descriptor) { + MSG_ERR(("ump_ukk_map_mem: descriptor allocation failed\n")); + return _MALI_OSK_ERR_NOMEM; + } + + handle = ump_dd_handle_create_from_secure_id(args->secure_id); + if ( UMP_DD_HANDLE_INVALID == handle) { + _mali_osk_free(descriptor); + DBG_MSG(1, ("Trying to map unknown secure ID %u\n", args->secure_id)); + return _MALI_OSK_ERR_FAULT; + } + + mem = (ump_dd_mem*)handle; + DEBUG_ASSERT(mem); + if (mem->size_bytes != args->size) { + _mali_osk_free(descriptor); + ump_dd_reference_release(handle); + DBG_MSG(1, ("Trying to map too much or little. ID: %u, virtual size=%lu, UMP size: %lu\n", args->secure_id, args->size, mem->size_bytes)); + return _MALI_OSK_ERR_FAULT; + } + + map_id = ump_descriptor_mapping_allocate_mapping( session_data->cookies_map, (void*) descriptor ); + + if (map_id < 0) { + _mali_osk_free(descriptor); + ump_dd_reference_release(handle); + DBG_MSG(1, ("ump_ukk_map_mem: unable to allocate a descriptor_mapping for return cookie\n")); + + return _MALI_OSK_ERR_NOMEM; + } + + descriptor->size = args->size; + descriptor->handle = handle; + descriptor->phys_addr = args->phys_addr; + descriptor->process_mapping_info = args->_ukk_private; + descriptor->ump_session = session_data; + descriptor->cookie = (u32)map_id; + + if ( mem->is_cached ) { + descriptor->is_cached = 1; + args->is_cached = 1; + DBG_MSG(3, ("Mapping UMP secure_id: %d as cached.\n", args->secure_id)); + } else { + descriptor->is_cached = 0; + args->is_cached = 0; + DBG_MSG(3, ("Mapping UMP secure_id: %d as Uncached.\n", args->secure_id)); + } + + _mali_osk_list_init( &descriptor->list ); + + err = _ump_osk_mem_mapregion_init( descriptor ); + if( _MALI_OSK_ERR_OK != err ) { + DBG_MSG(1, ("Failed to initialize memory mapping in _ump_ukk_map_mem(). ID: %u\n", args->secure_id)); + ump_descriptor_mapping_free( session_data->cookies_map, map_id ); + _mali_osk_free(descriptor); + ump_dd_reference_release(mem); + return err; + } + + DBG_MSG(4, ("Mapping virtual to physical memory: ID: %u, size:%lu, first physical addr: 0x%08lx, number of regions: %lu\n", + mem->secure_id, + mem->size_bytes, + ((NULL != mem->block_array) ? mem->block_array->addr : 0), + mem->nr_blocks)); + + left = descriptor->size; + /* loop over all blocks and map them in */ + for (block = 0; block < mem->nr_blocks; block++) { + unsigned long size_to_map; + + if (left > mem->block_array[block].size) { + size_to_map = mem->block_array[block].size; + } else { + size_to_map = left; + } + + if (_MALI_OSK_ERR_OK != _ump_osk_mem_mapregion_map(descriptor, offset, (u32 *)&(mem->block_array[block].addr), size_to_map ) ) { + DBG_MSG(1, ("WARNING: _ump_ukk_map_mem failed to map memory into userspace\n")); + ump_descriptor_mapping_free( session_data->cookies_map, map_id ); + ump_dd_reference_release(mem); + _ump_osk_mem_mapregion_term( descriptor ); + _mali_osk_free(descriptor); + return _MALI_OSK_ERR_FAULT; + } + left -= size_to_map; + offset += size_to_map; + } + + /* Add to the ump_memory_allocation tracking list */ + _mali_osk_mutex_wait(session_data->lock); + _mali_osk_list_add( &descriptor->list, &session_data->list_head_session_memory_mappings_list ); + _mali_osk_mutex_signal(session_data->lock); + + args->mapping = descriptor->mapping; + args->cookie = descriptor->cookie; + + return _MALI_OSK_ERR_OK; +} + +void _ump_ukk_unmap_mem( _ump_uk_unmap_mem_s *args ) +{ + struct ump_session_data * session_data; + ump_memory_allocation * descriptor; + ump_dd_handle handle; + + session_data = (ump_session_data *)args->ctx; + + if( NULL == session_data ) { + MSG_ERR(("Session data is NULL in _ump_ukk_map_mem()\n")); + return; + } + + if (0 != ump_descriptor_mapping_get( session_data->cookies_map, (int)args->cookie, (void**)&descriptor) ) { + MSG_ERR(("_ump_ukk_map_mem: cookie 0x%X not found for this session\n", args->cookie )); + return; + } + + DEBUG_ASSERT_POINTER(descriptor); + + handle = descriptor->handle; + if ( UMP_DD_HANDLE_INVALID == handle) { + DBG_MSG(1, ("WARNING: Trying to unmap unknown handle: UNKNOWN\n")); + return; + } + + /* Remove the ump_memory_allocation from the list of tracked mappings */ + _mali_osk_mutex_wait(session_data->lock); + _mali_osk_list_del( &descriptor->list ); + _mali_osk_mutex_signal(session_data->lock); + + ump_descriptor_mapping_free( session_data->cookies_map, (int)args->cookie ); + + ump_dd_reference_release(handle); + + _ump_osk_mem_mapregion_term( descriptor ); + _mali_osk_free(descriptor); +} + +u32 _ump_ukk_report_memory_usage( void ) +{ + if(device.backend->stat) + return device.backend->stat(device.backend); + else + return 0; +} diff --git a/drivers/gpu/arm/ump/common/ump_kernel_common.h b/drivers/gpu/arm/ump/common/ump_kernel_common.h new file mode 100644 index 00000000000000..fa4639ff0c74ea --- /dev/null +++ b/drivers/gpu/arm/ump/common/ump_kernel_common.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __UMP_KERNEL_COMMON_H__ +#define __UMP_KERNEL_COMMON_H__ + +#include "ump_kernel_types.h" +#include "ump_kernel_interface.h" +#include "ump_kernel_descriptor_mapping.h" +#include "ump_kernel_memory_backend.h" + + +#ifdef DEBUG +extern int ump_debug_level; +#define UMP_DEBUG_PRINT(args) _mali_osk_dbgmsg args +#define UMP_DEBUG_CODE(args) args +#define DBG_MSG(level,args) do { /* args should be in brackets */ \ + ((level) <= ump_debug_level)?\ + UMP_DEBUG_PRINT(("UMP<" #level ">: ")), \ + UMP_DEBUG_PRINT(args):0; \ + } while (0) + +#define DBG_MSG_IF(level,condition,args) /* args should be in brackets */ \ + if((condition)&&((level) <= ump_debug_level)) {\ + UMP_DEBUG_PRINT(("UMP<" #level ">: ")); \ + UMP_DEBUG_PRINT(args); \ + } + +#define DBG_MSG_ELSE(level,args) /* args should be in brackets */ \ + else if((level) <= ump_debug_level) { \ + UMP_DEBUG_PRINT(("UMP<" #level ">: ")); \ + UMP_DEBUG_PRINT(args); \ + } + +#define DEBUG_ASSERT_POINTER(pointer) do {if( (pointer)== NULL) MSG_ERR(("NULL pointer " #pointer)); } while(0) +#define DEBUG_ASSERT(condition) do {if(!(condition)) MSG_ERR(("ASSERT failed: " #condition)); } while(0) +#else /* DEBUG */ +#define UMP_DEBUG_PRINT(args) do {} while(0) +#define UMP_DEBUG_CODE(args) +#define DBG_MSG(level,args) do {} while(0) +#define DBG_MSG_IF(level,condition,args) do {} while(0) +#define DBG_MSG_ELSE(level,args) do {} while(0) +#define DEBUG_ASSERT(condition) do {} while(0) +#define DEBUG_ASSERT_POINTER(pointer) do {} while(0) +#endif /* DEBUG */ + +#define MSG_ERR(args) do{ /* args should be in brackets */ \ + _mali_osk_dbgmsg("UMP: ERR: %s\n" ,__FILE__); \ + _mali_osk_dbgmsg( " %s()%4d\n", __FUNCTION__, __LINE__) ; \ + _mali_osk_dbgmsg args ; \ + _mali_osk_dbgmsg("\n"); \ + } while(0) + +#define MSG(args) do{ /* args should be in brackets */ \ + _mali_osk_dbgmsg("UMP: "); \ + _mali_osk_dbgmsg args; \ + } while (0) + + + +/* + * This struct is used to store per session data. + * A session is created when someone open() the device, and + * closed when someone close() it or the user space application terminates. + */ +typedef struct ump_session_data { + _mali_osk_list_t list_head_session_memory_list; /**< List of ump allocations made by the process (elements are ump_session_memory_list_element) */ + _mali_osk_list_t list_head_session_memory_mappings_list; /**< List of ump_memory_allocations mapped in */ + int api_version; + _mali_osk_mutex_t *lock; + ump_descriptor_mapping * cookies_map; /**< Secure mapping of cookies from _ump_ukk_map_mem() */ + int cache_operations_ongoing; + int has_pending_level1_cache_flush; +} ump_session_data; + + + +/* + * This struct is used to track the UMP memory references a session has. + * We need to track this in order to be able to clean up after user space processes + * which don't do it themself (e.g. due to a crash or premature termination). + */ +typedef struct ump_session_memory_list_element { + struct ump_dd_mem * mem; + _mali_osk_list_t list; +} ump_session_memory_list_element; + + + +/* + * Device specific data, created when device driver is loaded, and then kept as the global variable device. + */ +typedef struct ump_dev { + _mali_osk_mutex_t *secure_id_map_lock; + ump_descriptor_mapping * secure_id_map; + ump_memory_backend * backend; +} ump_dev; + + + +extern int ump_debug_level; +extern struct ump_dev device; + +_mali_osk_errcode_t ump_kernel_constructor(void); +void ump_kernel_destructor(void); +int map_errcode( _mali_osk_errcode_t err ); + +/** + * variables from user space cannot be dereferenced from kernel space; tagging them + * with __user allows the GCC compiler to generate a warning. Other compilers may + * not support this so we define it here as an empty macro if the compiler doesn't + * define it. + */ +#ifndef __user +#define __user +#endif + +#endif /* __UMP_KERNEL_COMMON_H__ */ diff --git a/drivers/gpu/arm/ump/common/ump_kernel_descriptor_mapping.c b/drivers/gpu/arm/ump/common/ump_kernel_descriptor_mapping.c new file mode 100644 index 00000000000000..ab4e01e012019c --- /dev/null +++ b/drivers/gpu/arm/ump/common/ump_kernel_descriptor_mapping.c @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2010-2011, 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_osk_bitops.h" +#include "ump_kernel_common.h" +#include "ump_kernel_descriptor_mapping.h" + +#define MALI_PAD_INT(x) (((x) + (BITS_PER_LONG - 1)) & ~(BITS_PER_LONG - 1)) + +/** + * Allocate a descriptor table capable of holding 'count' mappings + * @param count Number of mappings in the table + * @return Pointer to a new table, NULL on error + */ +static ump_descriptor_table * descriptor_table_alloc(int count); + +/** + * Free a descriptor table + * @param table The table to free + */ +static void descriptor_table_free(ump_descriptor_table * table); + +ump_descriptor_mapping * ump_descriptor_mapping_create(int init_entries, int max_entries) +{ + ump_descriptor_mapping * map = _mali_osk_calloc(1, sizeof(ump_descriptor_mapping) ); + + init_entries = MALI_PAD_INT(init_entries); + max_entries = MALI_PAD_INT(max_entries); + + if (NULL != map) { + map->table = descriptor_table_alloc(init_entries); + if (NULL != map->table) { + map->lock = _mali_osk_mutex_rw_init(_MALI_OSK_LOCKFLAG_UNORDERED, 0); + if ( NULL != map->lock ) { + _mali_osk_set_nonatomic_bit(0, map->table->usage); /* reserve bit 0 to prevent NULL/zero logic to kick in */ + map->max_nr_mappings_allowed = max_entries; + map->current_nr_mappings = init_entries; + return map; + } + descriptor_table_free(map->table); + } + _mali_osk_free(map); + } + return NULL; +} + +void ump_descriptor_mapping_destroy(ump_descriptor_mapping * map) +{ + descriptor_table_free(map->table); + _mali_osk_mutex_rw_term(map->lock); + _mali_osk_free(map); +} + +int ump_descriptor_mapping_allocate_mapping(ump_descriptor_mapping * map, void * target) +{ + int descriptor = -1;/*-EFAULT;*/ + _mali_osk_mutex_rw_wait(map->lock, _MALI_OSK_LOCKMODE_RW); + descriptor = _mali_osk_find_first_zero_bit(map->table->usage, map->current_nr_mappings); + if (descriptor == map->current_nr_mappings) { + int nr_mappings_new; + /* no free descriptor, try to expand the table */ + ump_descriptor_table * new_table; + ump_descriptor_table * old_table = map->table; + nr_mappings_new= map->current_nr_mappings *2; + + if (map->current_nr_mappings >= map->max_nr_mappings_allowed) { + descriptor = -1; + goto unlock_and_exit; + } + + new_table = descriptor_table_alloc(nr_mappings_new); + if (NULL == new_table) { + descriptor = -1; + goto unlock_and_exit; + } + + _mali_osk_memcpy(new_table->usage, old_table->usage, (sizeof(unsigned long)*map->current_nr_mappings) / BITS_PER_LONG); + _mali_osk_memcpy(new_table->mappings, old_table->mappings, map->current_nr_mappings * sizeof(void*)); + map->table = new_table; + map->current_nr_mappings = nr_mappings_new; + descriptor_table_free(old_table); + } + + /* we have found a valid descriptor, set the value and usage bit */ + _mali_osk_set_nonatomic_bit(descriptor, map->table->usage); + map->table->mappings[descriptor] = target; + +unlock_and_exit: + _mali_osk_mutex_rw_signal(map->lock, _MALI_OSK_LOCKMODE_RW); + return descriptor; +} + +int ump_descriptor_mapping_get(ump_descriptor_mapping * map, int descriptor, void** target) +{ + int result = -1;/*-EFAULT;*/ + DEBUG_ASSERT(map); + _mali_osk_mutex_rw_wait(map->lock, _MALI_OSK_LOCKMODE_RO); + if ((descriptor > 0) && (descriptor < map->current_nr_mappings) && _mali_osk_test_bit(descriptor, map->table->usage)) { + *target = map->table->mappings[descriptor]; + result = 0; + } else *target = NULL; + _mali_osk_mutex_rw_signal(map->lock, _MALI_OSK_LOCKMODE_RO); + return result; +} + +int ump_descriptor_mapping_set(ump_descriptor_mapping * map, int descriptor, void * target) +{ + int result = -1;/*-EFAULT;*/ + _mali_osk_mutex_rw_wait(map->lock, _MALI_OSK_LOCKMODE_RO); + if ((descriptor > 0) && (descriptor < map->current_nr_mappings) && _mali_osk_test_bit(descriptor, map->table->usage)) { + map->table->mappings[descriptor] = target; + result = 0; + } + _mali_osk_mutex_rw_signal(map->lock, _MALI_OSK_LOCKMODE_RO); + return result; +} + +void ump_descriptor_mapping_free(ump_descriptor_mapping * map, int descriptor) +{ + _mali_osk_mutex_rw_wait(map->lock, _MALI_OSK_LOCKMODE_RW); + if ((descriptor > 0) && (descriptor < map->current_nr_mappings) && _mali_osk_test_bit(descriptor, map->table->usage)) { + map->table->mappings[descriptor] = NULL; + _mali_osk_clear_nonatomic_bit(descriptor, map->table->usage); + } + _mali_osk_mutex_rw_signal(map->lock, _MALI_OSK_LOCKMODE_RW); +} + +static ump_descriptor_table * descriptor_table_alloc(int count) +{ + ump_descriptor_table * table; + + table = _mali_osk_calloc(1, sizeof(ump_descriptor_table) + ((sizeof(unsigned long) * count)/BITS_PER_LONG) + (sizeof(void*) * count) ); + + if (NULL != table) { + table->usage = (u32*)((u8*)table + sizeof(ump_descriptor_table)); + table->mappings = (void**)((u8*)table + sizeof(ump_descriptor_table) + ((sizeof(unsigned long) * count)/BITS_PER_LONG)); + } + + return table; +} + +static void descriptor_table_free(ump_descriptor_table * table) +{ + _mali_osk_free(table); +} + diff --git a/drivers/gpu/arm/ump/common/ump_kernel_descriptor_mapping.h b/drivers/gpu/arm/ump/common/ump_kernel_descriptor_mapping.h new file mode 100644 index 00000000000000..a351d3ae311743 --- /dev/null +++ b/drivers/gpu/arm/ump/common/ump_kernel_descriptor_mapping.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2010-2011, 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_descriptor_mapping.h + */ + +#ifndef __UMP_KERNEL_DESCRIPTOR_MAPPING_H__ +#define __UMP_KERNEL_DESCRIPTOR_MAPPING_H__ + +#include "mali_osk.h" + +/** + * The actual descriptor mapping table, never directly accessed by clients + */ +typedef struct ump_descriptor_table { + u32 * usage; /**< Pointer to bitpattern indicating if a descriptor is valid/used or not */ + void** mappings; /**< Array of the pointers the descriptors map to */ +} ump_descriptor_table; + +/** + * The descriptor mapping object + * Provides a separate namespace where we can map an integer to a pointer + */ +typedef struct ump_descriptor_mapping { + _mali_osk_mutex_rw_t *lock; /**< Lock protecting access to the mapping object */ + int max_nr_mappings_allowed; /**< Max number of mappings to support in this namespace */ + int current_nr_mappings; /**< Current number of possible mappings */ + ump_descriptor_table * table; /**< Pointer to the current mapping table */ +} ump_descriptor_mapping; + +/** + * Create a descriptor mapping object + * Create a descriptor mapping capable of holding init_entries growable to max_entries + * @param init_entries Number of entries to preallocate memory for + * @param max_entries Number of entries to max support + * @return Pointer to a descriptor mapping object, NULL on failure + */ +ump_descriptor_mapping * ump_descriptor_mapping_create(int init_entries, int max_entries); + +/** + * Destroy a descriptor mapping object + * @param map The map to free + */ +void ump_descriptor_mapping_destroy(ump_descriptor_mapping * map); + +/** + * Allocate a new mapping entry (descriptor ID) + * Allocates a new entry in the map. + * @param map The map to allocate a new entry in + * @param target The value to map to + * @return The descriptor allocated, a negative value on error + */ +int ump_descriptor_mapping_allocate_mapping(ump_descriptor_mapping * map, void * target); + +/** + * Get the value mapped to by a descriptor ID + * @param map The map to lookup the descriptor id in + * @param descriptor The descriptor ID to lookup + * @param target Pointer to a pointer which will receive the stored value + * @return 0 on successful lookup, negative on error + */ +int ump_descriptor_mapping_get(ump_descriptor_mapping * map, int descriptor, void** target); + +/** + * Set the value mapped to by a descriptor ID + * @param map The map to lookup the descriptor id in + * @param descriptor The descriptor ID to lookup + * @param target Pointer to replace the current value with + * @return 0 on successful lookup, negative on error + */ +int ump_descriptor_mapping_set(ump_descriptor_mapping * map, int descriptor, void * target); + +/** + * Free the descriptor ID + * For the descriptor to be reused it has to be freed + * @param map The map to free the descriptor from + * @param descriptor The descriptor ID to free + */ +void ump_descriptor_mapping_free(ump_descriptor_mapping * map, int descriptor); + +#endif /* __UMP_KERNEL_DESCRIPTOR_MAPPING_H__ */ diff --git a/drivers/gpu/arm/ump/common/ump_kernel_memory_backend.h b/drivers/gpu/arm/ump/common/ump_kernel_memory_backend.h new file mode 100644 index 00000000000000..705c705d89c713 --- /dev/null +++ b/drivers/gpu/arm/ump/common/ump_kernel_memory_backend.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010-2011, 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_memory_mapping.h + */ + +#ifndef __UMP_KERNEL_MEMORY_BACKEND_H__ +#define __UMP_KERNEL_MEMORY_BACKEND_H__ + +#include "ump_kernel_interface.h" +#include "ump_kernel_types.h" + + +typedef struct ump_memory_allocation { + void * phys_addr; + void * mapping; + unsigned long size; + ump_dd_handle handle; + void * process_mapping_info; + u32 cookie; /**< necessary on some U/K interface implementations */ + struct ump_session_data * ump_session; /**< Session that this allocation belongs to */ + _mali_osk_list_t list; /**< List for linking together memory allocations into the session's memory head */ + u32 is_cached; +} ump_memory_allocation; + +typedef struct ump_memory_backend { + int (*allocate)(void* ctx, ump_dd_mem * descriptor); + void (*release)(void* ctx, ump_dd_mem * descriptor); + void (*shutdown)(struct ump_memory_backend * backend); + u32 (*stat)(struct ump_memory_backend *backend); + int (*pre_allocate_physical_check)(void *ctx, u32 size); + u32 (*adjust_to_mali_phys)(void *ctx, u32 cpu_phys); + void * ctx; +} ump_memory_backend; + +ump_memory_backend * ump_memory_backend_create ( void ); +void ump_memory_backend_destroy( void ); + +#endif /*__UMP_KERNEL_MEMORY_BACKEND_H__ */ + diff --git a/drivers/gpu/arm/ump/common/ump_kernel_ref_drv.c b/drivers/gpu/arm/ump/common/ump_kernel_ref_drv.c new file mode 100644 index 00000000000000..99eb17217b9863 --- /dev/null +++ b/drivers/gpu/arm/ump/common/ump_kernel_ref_drv.c @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_osk.h" +#include "mali_osk_list.h" +#include "ump_osk.h" +#include "ump_uk_types.h" + +#include "ump_kernel_interface_ref_drv.h" +#include "ump_kernel_common.h" +#include "ump_kernel_descriptor_mapping.h" + +#define UMP_MINIMUM_SIZE 4096 +#define UMP_MINIMUM_SIZE_MASK (~(UMP_MINIMUM_SIZE-1)) +#define UMP_SIZE_ALIGN(x) (((x)+UMP_MINIMUM_SIZE-1)&UMP_MINIMUM_SIZE_MASK) +#define UMP_ADDR_ALIGN_OFFSET(x) ((x)&(UMP_MINIMUM_SIZE-1)) +static void phys_blocks_release(void * ctx, struct ump_dd_mem * descriptor); + +UMP_KERNEL_API_EXPORT ump_dd_handle ump_dd_handle_create_from_phys_blocks(ump_dd_physical_block * blocks, unsigned long num_blocks) +{ + ump_dd_mem * mem; + unsigned long size_total = 0; + int map_id; + u32 i; + + /* Go through the input blocks and verify that they are sane */ + for (i=0; i < num_blocks; i++) { + unsigned long addr = blocks[i].addr; + unsigned long size = blocks[i].size; + + DBG_MSG(5, ("Adding physical memory to new handle. Address: 0x%08lx, size: %lu\n", addr, size)); + size_total += blocks[i].size; + + if (0 != UMP_ADDR_ALIGN_OFFSET(addr)) { + MSG_ERR(("Trying to create UMP memory from unaligned physical address. Address: 0x%08lx\n", addr)); + return UMP_DD_HANDLE_INVALID; + } + + if (0 != UMP_ADDR_ALIGN_OFFSET(size)) { + MSG_ERR(("Trying to create UMP memory with unaligned size. Size: %lu\n", size)); + return UMP_DD_HANDLE_INVALID; + } + } + + /* Allocate the ump_dd_mem struct for this allocation */ + mem = _mali_osk_malloc(sizeof(*mem)); + if (NULL == mem) { + DBG_MSG(1, ("Could not allocate ump_dd_mem in ump_dd_handle_create_from_phys_blocks()\n")); + return UMP_DD_HANDLE_INVALID; + } + + /* Find a secure ID for this allocation */ + _mali_osk_mutex_wait(device.secure_id_map_lock); + map_id = ump_descriptor_mapping_allocate_mapping(device.secure_id_map, (void*) mem); + + if (map_id < 0) { + _mali_osk_mutex_signal(device.secure_id_map_lock); + _mali_osk_free(mem); + DBG_MSG(1, ("Failed to allocate secure ID in ump_dd_handle_create_from_phys_blocks()\n")); + return UMP_DD_HANDLE_INVALID; + } + + /* Now, make a copy of the block information supplied by the user */ + mem->block_array = _mali_osk_malloc(sizeof(ump_dd_physical_block)* num_blocks); + if (NULL == mem->block_array) { + ump_descriptor_mapping_free(device.secure_id_map, map_id); + _mali_osk_mutex_signal(device.secure_id_map_lock); + _mali_osk_free(mem); + DBG_MSG(1, ("Could not allocate a mem handle for function ump_dd_handle_create_from_phys_blocks().\n")); + return UMP_DD_HANDLE_INVALID; + } + + _mali_osk_memcpy(mem->block_array, blocks, sizeof(ump_dd_physical_block) * num_blocks); + + /* And setup the rest of the ump_dd_mem struct */ + _mali_osk_atomic_init(&mem->ref_count, 1); + mem->secure_id = (ump_secure_id)map_id; + mem->size_bytes = size_total; + mem->nr_blocks = num_blocks; + mem->backend_info = NULL; + mem->ctx = NULL; + mem->release_func = phys_blocks_release; + /* For now UMP handles created by ump_dd_handle_create_from_phys_blocks() is forced to be Uncached */ + mem->is_cached = 0; + mem->hw_device = _UMP_UK_USED_BY_CPU; + mem->lock_usage = UMP_NOT_LOCKED; + + _mali_osk_mutex_signal(device.secure_id_map_lock); + DBG_MSG(3, ("UMP memory created. ID: %u, size: %lu\n", mem->secure_id, mem->size_bytes)); + + return (ump_dd_handle)mem; +} + +static void phys_blocks_release(void * ctx, struct ump_dd_mem * descriptor) +{ + _mali_osk_free(descriptor->block_array); + descriptor->block_array = NULL; +} + +_mali_osk_errcode_t _ump_ukk_allocate( _ump_uk_allocate_s *user_interaction ) +{ + ump_session_data * session_data = NULL; + ump_dd_mem *new_allocation = NULL; + ump_session_memory_list_element * session_memory_element = NULL; + int map_id; + + DEBUG_ASSERT_POINTER( user_interaction ); + DEBUG_ASSERT_POINTER( user_interaction->ctx ); + + session_data = (ump_session_data *) user_interaction->ctx; + + session_memory_element = _mali_osk_calloc( 1, sizeof(ump_session_memory_list_element)); + if (NULL == session_memory_element) { + DBG_MSG(1, ("Failed to allocate ump_session_memory_list_element in ump_ioctl_allocate()\n")); + return _MALI_OSK_ERR_NOMEM; + } + + + new_allocation = _mali_osk_calloc( 1, sizeof(ump_dd_mem)); + if (NULL==new_allocation) { + _mali_osk_free(session_memory_element); + DBG_MSG(1, ("Failed to allocate ump_dd_mem in _ump_ukk_allocate()\n")); + return _MALI_OSK_ERR_NOMEM; + } + + /* Create a secure ID for this allocation */ + _mali_osk_mutex_wait(device.secure_id_map_lock); + map_id = ump_descriptor_mapping_allocate_mapping(device.secure_id_map, (void*)new_allocation); + + if (map_id < 0) { + _mali_osk_mutex_signal(device.secure_id_map_lock); + _mali_osk_free(session_memory_element); + _mali_osk_free(new_allocation); + DBG_MSG(1, ("Failed to allocate secure ID in ump_ioctl_allocate()\n")); + return - _MALI_OSK_ERR_INVALID_FUNC; + } + + /* Initialize the part of the new_allocation that we know so for */ + new_allocation->secure_id = (ump_secure_id)map_id; + _mali_osk_atomic_init(&new_allocation->ref_count,1); + if ( 0==(UMP_REF_DRV_UK_CONSTRAINT_USE_CACHE & user_interaction->constraints) ) + new_allocation->is_cached = 0; + else new_allocation->is_cached = 1; + + /* special case a size of 0, we should try to emulate what malloc does in this case, which is to return a valid pointer that must be freed, but can't be dereferences */ + if (0 == user_interaction->size) { + user_interaction->size = 1; /* emulate by actually allocating the minimum block size */ + } + + new_allocation->size_bytes = UMP_SIZE_ALIGN(user_interaction->size); /* Page align the size */ + new_allocation->lock_usage = UMP_NOT_LOCKED; + + /* Now, ask the active memory backend to do the actual memory allocation */ + if (!device.backend->allocate( device.backend->ctx, new_allocation ) ) { + DBG_MSG(3, ("OOM: No more UMP memory left. Failed to allocate memory in ump_ioctl_allocate(). Size: %lu, requested size: %lu\n", new_allocation->size_bytes, (unsigned long)user_interaction->size)); + ump_descriptor_mapping_free(device.secure_id_map, map_id); + _mali_osk_mutex_signal(device.secure_id_map_lock); + _mali_osk_free(new_allocation); + _mali_osk_free(session_memory_element); + return _MALI_OSK_ERR_INVALID_FUNC; + } + new_allocation->hw_device = _UMP_UK_USED_BY_CPU; + new_allocation->ctx = device.backend->ctx; + new_allocation->release_func = device.backend->release; + + _mali_osk_mutex_signal(device.secure_id_map_lock); + + /* Initialize the session_memory_element, and add it to the session object */ + session_memory_element->mem = new_allocation; + _mali_osk_mutex_wait(session_data->lock); + _mali_osk_list_add(&(session_memory_element->list), &(session_data->list_head_session_memory_list)); + _mali_osk_mutex_signal(session_data->lock); + + user_interaction->secure_id = new_allocation->secure_id; + user_interaction->size = new_allocation->size_bytes; + DBG_MSG(3, ("UMP memory allocated. ID: %u, size: %lu\n", new_allocation->secure_id, new_allocation->size_bytes)); + + return _MALI_OSK_ERR_OK; +} diff --git a/drivers/gpu/arm/ump/common/ump_kernel_types.h b/drivers/gpu/arm/ump/common/ump_kernel_types.h new file mode 100644 index 00000000000000..08704ab4053feb --- /dev/null +++ b/drivers/gpu/arm/ump/common/ump_kernel_types.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __UMP_KERNEL_TYPES_H__ +#define __UMP_KERNEL_TYPES_H__ + +#include "ump_kernel_interface.h" +#include "mali_osk.h" + + +typedef enum { + UMP_USED_BY_CPU = 0, + UMP_USED_BY_MALI = 1, + UMP_USED_BY_UNKNOWN_DEVICE= 100, +} ump_hw_usage; + +typedef enum { + UMP_NOT_LOCKED = 0, + UMP_READ = 1, + UMP_READ_WRITE = 3, +} ump_lock_usage; + + +/* + * This struct is what is "behind" a ump_dd_handle + */ +typedef struct ump_dd_mem { + ump_secure_id secure_id; + _mali_osk_atomic_t ref_count; + unsigned long size_bytes; + unsigned long nr_blocks; + ump_dd_physical_block * block_array; + void (*release_func)(void * ctx, struct ump_dd_mem * descriptor); + void * ctx; + void * backend_info; + int is_cached; + ump_hw_usage hw_device; + ump_lock_usage lock_usage; +} ump_dd_mem; + + + +#endif /* __UMP_KERNEL_TYPES_H__ */ diff --git a/drivers/gpu/arm/ump/common/ump_osk.h b/drivers/gpu/arm/ump/common/ump_osk.h new file mode 100644 index 00000000000000..9b048aa312b18d --- /dev/null +++ b/drivers/gpu/arm/ump/common/ump_osk.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_osk.h + * Defines the OS abstraction layer for the UMP kernel device driver (OSK) + */ + +#ifndef __UMP_OSK_H__ +#define __UMP_OSK_H__ + +#include +#include +#include "ump_uk_types.h" +#include "ump_kernel_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +_mali_osk_errcode_t _ump_osk_init( void ); + +_mali_osk_errcode_t _ump_osk_term( void ); + +int _ump_osk_atomic_inc_and_read( _mali_osk_atomic_t *atom ); + +int _ump_osk_atomic_dec_and_read( _mali_osk_atomic_t *atom ); + +_mali_osk_errcode_t _ump_osk_mem_mapregion_init( ump_memory_allocation *descriptor ); + +_mali_osk_errcode_t _ump_osk_mem_mapregion_map( ump_memory_allocation * descriptor, u32 offset, u32 * phys_addr, unsigned long size ); + +void _ump_osk_mem_mapregion_term( ump_memory_allocation * descriptor ); + +void _ump_osk_msync( ump_dd_mem * mem, void * virt, u32 offset, u32 size, ump_uk_msync_op op, ump_session_data * session_data ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/drivers/gpu/arm/ump/common/ump_uk_types.h b/drivers/gpu/arm/ump/common/ump_uk_types.h new file mode 100644 index 00000000000000..ead12584cddb28 --- /dev/null +++ b/drivers/gpu/arm/ump/common/ump_uk_types.h @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2010, 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_uk_types.h + * Defines the types and constants used in the user-kernel interface + */ + +#ifndef __UMP_UK_TYPES_H__ +#define __UMP_UK_TYPES_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Helpers for API version handling */ +#define MAKE_VERSION_ID(x) (((x) << 16UL) | (x)) +#define IS_VERSION_ID(x) (((x) & 0xFFFF) == (((x) >> 16UL) & 0xFFFF)) +#define GET_VERSION(x) (((x) >> 16UL) & 0xFFFF) +#define IS_API_MATCH(x, y) (IS_VERSION_ID((x)) && IS_VERSION_ID((y)) && (GET_VERSION((x)) == GET_VERSION((y)))) + +/** + * API version define. + * Indicates the version of the kernel API + * The version is a 16bit integer incremented on each API change. + * The 16bit integer is stored twice in a 32bit integer + * So for version 1 the value would be 0x00010001 + */ +#define UMP_IOCTL_API_VERSION MAKE_VERSION_ID(2) + +typedef enum +{ + _UMP_IOC_QUERY_API_VERSION = 1, + _UMP_IOC_ALLOCATE, + _UMP_IOC_RELEASE, + _UMP_IOC_SIZE_GET, + _UMP_IOC_MAP_MEM, /* not used in Linux */ + _UMP_IOC_UNMAP_MEM, /* not used in Linux */ + _UMP_IOC_MSYNC, + _UMP_IOC_CACHE_OPERATIONS_CONTROL, + _UMP_IOC_SWITCH_HW_USAGE, + _UMP_IOC_LOCK, + _UMP_IOC_UNLOCK, +} _ump_uk_functions; + +typedef enum +{ + UMP_REF_DRV_UK_CONSTRAINT_NONE = 0, + UMP_REF_DRV_UK_CONSTRAINT_PHYSICALLY_LINEAR = 1, + UMP_REF_DRV_UK_CONSTRAINT_USE_CACHE = 4, +} ump_uk_alloc_constraints; + +typedef enum +{ + _UMP_UK_MSYNC_CLEAN = 0, + _UMP_UK_MSYNC_CLEAN_AND_INVALIDATE = 1, + _UMP_UK_MSYNC_INVALIDATE = 2, + _UMP_UK_MSYNC_FLUSH_L1 = 3, + _UMP_UK_MSYNC_READOUT_CACHE_ENABLED = 128, +} ump_uk_msync_op; + +typedef enum +{ + _UMP_UK_CACHE_OP_START = 0, + _UMP_UK_CACHE_OP_FINISH = 1, +} ump_uk_cache_op_control; + +typedef enum +{ + _UMP_UK_READ = 1, + _UMP_UK_READ_WRITE = 3, +} ump_uk_lock_usage; + +typedef enum +{ + _UMP_UK_USED_BY_CPU = 0, + _UMP_UK_USED_BY_MALI = 1, + _UMP_UK_USED_BY_UNKNOWN_DEVICE = 100, +} ump_uk_user; + +/** + * Get API version ([in,out] u32 api_version, [out] u32 compatible) + */ +typedef struct _ump_uk_api_version_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 version; /**< Set to the user space version on entry, stores the device driver version on exit */ + u32 compatible; /**< Non-null if the device is compatible with the client */ +} _ump_uk_api_version_s; + +/** + * ALLOCATE ([out] u32 secure_id, [in,out] u32 size, [in] contraints) + */ +typedef struct _ump_uk_allocate_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 secure_id; /**< Return value from DD to Userdriver */ + u32 size; /**< Input and output. Requested size; input. Returned size; output */ + ump_uk_alloc_constraints constraints; /**< Only input to Devicedriver */ +} _ump_uk_allocate_s; + +/** + * SIZE_GET ([in] u32 secure_id, [out]size ) + */ +typedef struct _ump_uk_size_get_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 secure_id; /**< Input to DD */ + u32 size; /**< Returned size; output */ +} _ump_uk_size_get_s; + +/** + * Release ([in] u32 secure_id) + */ +typedef struct _ump_uk_release_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 secure_id; /**< Input to DD */ +} _ump_uk_release_s; + +typedef struct _ump_uk_map_mem_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + void *mapping; /**< [out] Returns user-space virtual address for the mapping */ + void *phys_addr; /**< [in] physical address */ + unsigned long size; /**< [in] size */ + u32 secure_id; /**< [in] secure_id to assign to mapping */ + void *_ukk_private; /**< Only used inside linux port between kernel frontend and common part to store vma */ + u32 cookie; + u32 is_cached; /**< [in,out] caching of CPU mappings */ +} _ump_uk_map_mem_s; + +typedef struct _ump_uk_unmap_mem_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + void *mapping; + u32 size; + void *_ukk_private; + u32 cookie; +} _ump_uk_unmap_mem_s; + +typedef struct _ump_uk_msync_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + void *mapping; /**< [in] mapping addr */ + void *address; /**< [in] flush start addr */ + u32 size; /**< [in] size to flush */ + ump_uk_msync_op op; /**< [in] flush operation */ + u32 cookie; /**< [in] cookie stored with reference to the kernel mapping internals */ + u32 secure_id; /**< [in] secure_id that identifies the ump buffer */ + u32 is_cached; /**< [out] caching of CPU mappings */ +} _ump_uk_msync_s; + +typedef struct _ump_uk_cache_operations_control_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + ump_uk_cache_op_control op; /**< [in] cache operations start/stop */ +} _ump_uk_cache_operations_control_s; + + +typedef struct _ump_uk_switch_hw_usage_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 secure_id; /**< [in] secure_id that identifies the ump buffer */ + ump_uk_user new_user; /**< [in] cookie stored with reference to the kernel mapping internals */ + +} _ump_uk_switch_hw_usage_s; + +typedef struct _ump_uk_lock_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 secure_id; /**< [in] secure_id that identifies the ump buffer */ + ump_uk_lock_usage lock_usage; +} _ump_uk_lock_s; + +typedef struct _ump_uk_unlock_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 secure_id; /**< [in] secure_id that identifies the ump buffer */ +} _ump_uk_unlock_s; + +#ifdef __cplusplus +} +#endif + +#endif /* __UMP_UK_TYPES_H__ */ diff --git a/drivers/gpu/arm/ump/common/ump_ukk.h b/drivers/gpu/arm/ump/common/ump_ukk.h new file mode 100644 index 00000000000000..568a428d3c0f69 --- /dev/null +++ b/drivers/gpu/arm/ump/common/ump_ukk.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_ukk.h + * Defines the kernel-side interface of the user-kernel interface + */ + +#ifndef __UMP_UKK_H__ +#define __UMP_UKK_H__ + +#include "mali_osk.h" +#include "ump_uk_types.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +_mali_osk_errcode_t _ump_ukk_open( void** context ); + +_mali_osk_errcode_t _ump_ukk_close( void** context ); + +_mali_osk_errcode_t _ump_ukk_allocate( _ump_uk_allocate_s *user_interaction ); + +_mali_osk_errcode_t _ump_ukk_release( _ump_uk_release_s *release_info ); + +_mali_osk_errcode_t _ump_ukk_size_get( _ump_uk_size_get_s *user_interaction ); + +_mali_osk_errcode_t _ump_ukk_map_mem( _ump_uk_map_mem_s *args ); + +_mali_osk_errcode_t _ump_uku_get_api_version( _ump_uk_api_version_s *args ); + +void _ump_ukk_unmap_mem( _ump_uk_unmap_mem_s *args ); + +void _ump_ukk_msync( _ump_uk_msync_s *args ); + +void _ump_ukk_cache_operations_control(_ump_uk_cache_operations_control_s* args); + +void _ump_ukk_switch_hw_usage(_ump_uk_switch_hw_usage_s *args ); + +void _ump_ukk_lock(_ump_uk_lock_s *args ); + +void _ump_ukk_unlock(_ump_uk_unlock_s *args ); + +u32 _ump_ukk_report_memory_usage( void ); + +#ifdef __cplusplus +} +#endif + +#endif /* __UMP_UKK_H__ */ diff --git a/drivers/gpu/arm/ump/linux/license/gpl/ump_kernel_license.h b/drivers/gpu/arm/ump/linux/license/gpl/ump_kernel_license.h new file mode 100644 index 00000000000000..4db45388c26b2c --- /dev/null +++ b/drivers/gpu/arm/ump/linux/license/gpl/ump_kernel_license.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2010, 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_license.h + * Defines for the macro MODULE_LICENSE. + */ + +#ifndef __UMP_KERNEL_LICENSE_H__ +#define __UMP_KERNEL_LICENSE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define UMP_KERNEL_LINUX_LICENSE "GPL" +#define UMP_LICENSE_IS_GPL 1 + +#ifdef __cplusplus +} +#endif + +#endif /* __UMP_KERNEL_LICENSE_H__ */ diff --git a/drivers/gpu/arm/ump/linux/ump_ioctl.h b/drivers/gpu/arm/ump/linux/ump_ioctl.h new file mode 100644 index 00000000000000..51787f63515b0d --- /dev/null +++ b/drivers/gpu/arm/ump/linux/ump_ioctl.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __UMP_IOCTL_H__ +#define __UMP_IOCTL_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include + +#ifndef __user +#define __user +#endif + + +/** + * @file UMP_ioctl.h + * This file describes the interface needed to use the Linux device driver. + * The interface is used by the userpace UMP driver. + */ + +#define UMP_IOCTL_NR 0x90 + + +#define UMP_IOC_QUERY_API_VERSION _IOR(UMP_IOCTL_NR, _UMP_IOC_QUERY_API_VERSION, _ump_uk_api_version_s) +#define UMP_IOC_ALLOCATE _IOWR(UMP_IOCTL_NR, _UMP_IOC_ALLOCATE, _ump_uk_allocate_s) +#define UMP_IOC_RELEASE _IOR(UMP_IOCTL_NR, _UMP_IOC_RELEASE, _ump_uk_release_s) +#define UMP_IOC_SIZE_GET _IOWR(UMP_IOCTL_NR, _UMP_IOC_SIZE_GET, _ump_uk_size_get_s) +#define UMP_IOC_MSYNC _IOW(UMP_IOCTL_NR, _UMP_IOC_MSYNC, _ump_uk_msync_s) + +#define UMP_IOC_CACHE_OPERATIONS_CONTROL _IOW(UMP_IOCTL_NR, _UMP_IOC_CACHE_OPERATIONS_CONTROL, _ump_uk_cache_operations_control_s) +#define UMP_IOC_SWITCH_HW_USAGE _IOW(UMP_IOCTL_NR, _UMP_IOC_SWITCH_HW_USAGE, _ump_uk_switch_hw_usage_s) +#define UMP_IOC_LOCK _IOW(UMP_IOCTL_NR, _UMP_IOC_LOCK, _ump_uk_lock_s) +#define UMP_IOC_UNLOCK _IOW(UMP_IOCTL_NR, _UMP_IOC_UNLOCK, _ump_uk_unlock_s) + + +#ifdef __cplusplus +} +#endif + +#endif /* __UMP_IOCTL_H__ */ diff --git a/drivers/gpu/arm/ump/linux/ump_kernel_linux.c b/drivers/gpu/arm/ump/linux/ump_kernel_linux.c new file mode 100644 index 00000000000000..b22585da7c85ca --- /dev/null +++ b/drivers/gpu/arm/ump/linux/ump_kernel_linux.c @@ -0,0 +1,447 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include /* kernel module definitions */ +#include /* file system operations */ +#include /* character device definitions */ +#include /* request_mem_region */ +#include /* memory management functions and types */ +#include /* user space access */ +#include +#include +#include + +#include "arch/config.h" /* Configuration for current platform. The symlinc for arch is set by Makefile */ +#include "ump_ioctl.h" +#include "ump_kernel_common.h" +#include "ump_kernel_interface.h" +#include "ump_kernel_interface_ref_drv.h" +#include "ump_kernel_descriptor_mapping.h" +#include "ump_kernel_memory_backend.h" +#include "ump_kernel_memory_backend_os.h" +#include "ump_kernel_memory_backend_dedicated.h" +#include "ump_kernel_license.h" + +#include "ump_osk.h" +#include "ump_ukk.h" +#include "ump_uk_types.h" +#include "ump_ukk_wrappers.h" +#include "ump_ukk_ref_wrappers.h" + + +/* Module parameter to control log level */ +int ump_debug_level = 2; +module_param(ump_debug_level, int, S_IRUSR | S_IWUSR | S_IWGRP | S_IRGRP | S_IROTH); /* rw-rw-r-- */ +MODULE_PARM_DESC(ump_debug_level, "Higher number, more dmesg output"); + +/* By default the module uses any available major, but it's possible to set it at load time to a specific number */ +int ump_major = 0; +module_param(ump_major, int, S_IRUGO); /* r--r--r-- */ +MODULE_PARM_DESC(ump_major, "Device major number"); + +/* Name of the UMP device driver */ +static char ump_dev_name[] = "ump"; /* should be const, but the functions we call requires non-cost */ + + +#if UMP_LICENSE_IS_GPL +static struct dentry *ump_debugfs_dir = NULL; +#endif + +/* + * The data which we attached to each virtual memory mapping request we get. + * Each memory mapping has a reference to the UMP memory it maps. + * We release this reference when the last memory mapping is unmapped. + */ +typedef struct ump_vma_usage_tracker { + int references; + ump_dd_handle handle; +} ump_vma_usage_tracker; + +struct ump_device { + struct cdev cdev; +#if UMP_LICENSE_IS_GPL + struct class * ump_class; +#endif +}; + +/* The global variable containing the global device data */ +static struct ump_device ump_device; + + +/* Forward declare static functions */ +static int ump_file_open(struct inode *inode, struct file *filp); +static int ump_file_release(struct inode *inode, struct file *filp); +#ifdef HAVE_UNLOCKED_IOCTL +static long ump_file_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +#else +static int ump_file_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); +#endif +static int ump_file_mmap(struct file * filp, struct vm_area_struct * vma); + + +/* This variable defines the file operations this UMP device driver offer */ +static struct file_operations ump_fops = { + .owner = THIS_MODULE, + .open = ump_file_open, + .release = ump_file_release, +#ifdef HAVE_UNLOCKED_IOCTL + .unlocked_ioctl = ump_file_ioctl, +#else + .ioctl = ump_file_ioctl, +#endif + .mmap = ump_file_mmap +}; + + +/* This function is called by Linux to initialize this module. + * All we do is initialize the UMP device driver. + */ +static int ump_initialize_module(void) +{ + _mali_osk_errcode_t err; + + DBG_MSG(2, ("Inserting UMP device driver. Compiled: %s, time: %s\n", __DATE__, __TIME__)); + + err = ump_kernel_constructor(); + if (_MALI_OSK_ERR_OK != err) { + MSG_ERR(("UMP device driver init failed\n")); + return map_errcode(err); + } + + MSG(("UMP device driver %s loaded\n", SVN_REV_STRING)); + return 0; +} + + + +/* + * This function is called by Linux to unload/terminate/exit/cleanup this module. + * All we do is terminate the UMP device driver. + */ +static void ump_cleanup_module(void) +{ + DBG_MSG(2, ("Unloading UMP device driver\n")); + ump_kernel_destructor(); + DBG_MSG(2, ("Module unloaded\n")); +} + + + +static ssize_t ump_memory_used_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char buf[64]; + size_t r; + u32 mem = _ump_ukk_report_memory_usage(); + + r = snprintf(buf, 64, "%u\n", mem); + return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); +} + +static const struct file_operations ump_memory_usage_fops = { + .owner = THIS_MODULE, + .read = ump_memory_used_read, +}; + +/* + * Initialize the UMP device driver. + */ +int ump_kernel_device_initialize(void) +{ + int err; + dev_t dev = 0; +#if UMP_LICENSE_IS_GPL + ump_debugfs_dir = debugfs_create_dir(ump_dev_name, NULL); + if (ERR_PTR(-ENODEV) == ump_debugfs_dir) { + ump_debugfs_dir = NULL; + } else { + debugfs_create_file("memory_usage", 0400, ump_debugfs_dir, NULL, &ump_memory_usage_fops); + } +#endif + + if (0 == ump_major) { + /* auto select a major */ + err = alloc_chrdev_region(&dev, 0, 1, ump_dev_name); + ump_major = MAJOR(dev); + } else { + /* use load time defined major number */ + dev = MKDEV(ump_major, 0); + err = register_chrdev_region(dev, 1, ump_dev_name); + } + + if (0 == err) { + memset(&ump_device, 0, sizeof(ump_device)); + + /* initialize our char dev data */ + cdev_init(&ump_device.cdev, &ump_fops); + ump_device.cdev.owner = THIS_MODULE; + ump_device.cdev.ops = &ump_fops; + + /* register char dev with the kernel */ + err = cdev_add(&ump_device.cdev, dev, 1/*count*/); + if (0 == err) { + +#if UMP_LICENSE_IS_GPL + ump_device.ump_class = class_create(THIS_MODULE, ump_dev_name); + if (IS_ERR(ump_device.ump_class)) { + err = PTR_ERR(ump_device.ump_class); + } else { + struct device * mdev; + mdev = device_create(ump_device.ump_class, NULL, dev, NULL, ump_dev_name); + if (!IS_ERR(mdev)) { + return 0; + } + + err = PTR_ERR(mdev); + } + cdev_del(&ump_device.cdev); +#else + return 0; +#endif + } + + unregister_chrdev_region(dev, 1); + } + + return err; +} + + + +/* + * Terminate the UMP device driver + */ +void ump_kernel_device_terminate(void) +{ + dev_t dev = MKDEV(ump_major, 0); + +#if UMP_LICENSE_IS_GPL + device_destroy(ump_device.ump_class, dev); + class_destroy(ump_device.ump_class); +#endif + + /* unregister char device */ + cdev_del(&ump_device.cdev); + + /* free major */ + unregister_chrdev_region(dev, 1); + +#if UMP_LICENSE_IS_GPL + if(ump_debugfs_dir) + debugfs_remove_recursive(ump_debugfs_dir); +#endif +} + +/* + * Open a new session. User space has called open() on us. + */ +static int ump_file_open(struct inode *inode, struct file *filp) +{ + struct ump_session_data * session_data; + _mali_osk_errcode_t err; + + /* input validation */ + if (0 != MINOR(inode->i_rdev)) { + MSG_ERR(("Minor not zero in ump_file_open()\n")); + return -ENODEV; + } + + /* Call the OS-Independent UMP Open function */ + err = _ump_ukk_open((void**) &session_data ); + if( _MALI_OSK_ERR_OK != err ) { + MSG_ERR(("Ump failed to open a new session\n")); + return map_errcode( err ); + } + + filp->private_data = (void*)session_data; + filp->f_pos = 0; + + return 0; /* success */ +} + + + +/* + * Close a session. User space has called close() or crashed/terminated. + */ +static int ump_file_release(struct inode *inode, struct file *filp) +{ + _mali_osk_errcode_t err; + + err = _ump_ukk_close((void**) &filp->private_data ); + if( _MALI_OSK_ERR_OK != err ) { + return map_errcode( err ); + } + + return 0; /* success */ +} + + + +/* + * Handle IOCTL requests. + */ +#ifdef HAVE_UNLOCKED_IOCTL +static long ump_file_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +#else +static int ump_file_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +#endif +{ + int err = -ENOTTY; + void __user * argument; + struct ump_session_data * session_data; + +#ifndef HAVE_UNLOCKED_IOCTL + (void)inode; /* inode not used */ +#endif + + session_data = (struct ump_session_data *)filp->private_data; + if (NULL == session_data) { + MSG_ERR(("No session data attached to file object\n")); + return -ENOTTY; + } + + /* interpret the argument as a user pointer to something */ + argument = (void __user *)arg; + + switch (cmd) { + case UMP_IOC_QUERY_API_VERSION: + err = ump_get_api_version_wrapper((u32 __user *)argument, session_data); + break; + + case UMP_IOC_ALLOCATE : + err = ump_allocate_wrapper((u32 __user *)argument, session_data); + break; + + case UMP_IOC_RELEASE: + err = ump_release_wrapper((u32 __user *)argument, session_data); + break; + + case UMP_IOC_SIZE_GET: + err = ump_size_get_wrapper((u32 __user *)argument, session_data); + break; + + case UMP_IOC_MSYNC: + err = ump_msync_wrapper((u32 __user *)argument, session_data); + break; + + case UMP_IOC_CACHE_OPERATIONS_CONTROL: + err = ump_cache_operations_control_wrapper((u32 __user *)argument, session_data); + break; + + case UMP_IOC_SWITCH_HW_USAGE: + err = ump_switch_hw_usage_wrapper((u32 __user *)argument, session_data); + break; + + case UMP_IOC_LOCK: + err = ump_lock_wrapper((u32 __user *)argument, session_data); + break; + + case UMP_IOC_UNLOCK: + err = ump_unlock_wrapper((u32 __user *)argument, session_data); + break; + + default: + DBG_MSG(1, ("No handler for IOCTL. cmd: 0x%08x, arg: 0x%08lx\n", cmd, arg)); + err = -EFAULT; + break; + } + + return err; +} + +int map_errcode( _mali_osk_errcode_t err ) +{ + switch(err) { + case _MALI_OSK_ERR_OK : + return 0; + case _MALI_OSK_ERR_FAULT: + return -EFAULT; + case _MALI_OSK_ERR_INVALID_FUNC: + return -ENOTTY; + case _MALI_OSK_ERR_INVALID_ARGS: + return -EINVAL; + case _MALI_OSK_ERR_NOMEM: + return -ENOMEM; + case _MALI_OSK_ERR_TIMEOUT: + return -ETIMEDOUT; + case _MALI_OSK_ERR_RESTARTSYSCALL: + return -ERESTARTSYS; + case _MALI_OSK_ERR_ITEM_NOT_FOUND: + return -ENOENT; + default: + return -EFAULT; + } +} + +/* + * Handle from OS to map specified virtual memory to specified UMP memory. + */ +static int ump_file_mmap(struct file * filp, struct vm_area_struct * vma) +{ + _ump_uk_map_mem_s args; + _mali_osk_errcode_t err; + struct ump_session_data * session_data; + + /* Validate the session data */ + session_data = (struct ump_session_data *)filp->private_data; + if (NULL == session_data) { + MSG_ERR(("mmap() called without any session data available\n")); + return -EFAULT; + } + + /* Re-pack the arguments that mmap() packed for us */ + args.ctx = session_data; + args.phys_addr = 0; + args.size = vma->vm_end - vma->vm_start; + args._ukk_private = vma; + args.secure_id = vma->vm_pgoff; + args.is_cached = 0; + + if (!(vma->vm_flags & VM_SHARED)) { + args.is_cached = 1; + vma->vm_flags = vma->vm_flags | VM_SHARED | VM_MAYSHARE ; + DBG_MSG(3, ("UMP Map function: Forcing the CPU to use cache\n")); + } + /* By setting this flag, during a process fork; the child process will not have the parent UMP mappings */ + vma->vm_flags |= VM_DONTCOPY; + + DBG_MSG(4, ("UMP vma->flags: %x\n", vma->vm_flags )); + + /* Call the common mmap handler */ + err = _ump_ukk_map_mem( &args ); + if ( _MALI_OSK_ERR_OK != err) { + MSG_ERR(("_ump_ukk_map_mem() failed in function ump_file_mmap()")); + return map_errcode( err ); + } + + return 0; /* success */ +} + +/* Export UMP kernel space API functions */ +EXPORT_SYMBOL(ump_dd_secure_id_get); +EXPORT_SYMBOL(ump_dd_handle_create_from_secure_id); +EXPORT_SYMBOL(ump_dd_phys_block_count_get); +EXPORT_SYMBOL(ump_dd_phys_block_get); +EXPORT_SYMBOL(ump_dd_phys_blocks_get); +EXPORT_SYMBOL(ump_dd_size_get); +EXPORT_SYMBOL(ump_dd_reference_add); +EXPORT_SYMBOL(ump_dd_reference_release); + +/* Export our own extended kernel space allocator */ +EXPORT_SYMBOL(ump_dd_handle_create_from_phys_blocks); + +/* Setup init and exit functions for this module */ +module_init(ump_initialize_module); +module_exit(ump_cleanup_module); + +/* And some module informatio */ +MODULE_LICENSE(UMP_KERNEL_LINUX_LICENSE); +MODULE_AUTHOR("ARM Ltd."); +MODULE_VERSION(SVN_REV_STRING); diff --git a/drivers/gpu/arm/ump/linux/ump_kernel_linux.h b/drivers/gpu/arm/ump/linux/ump_kernel_linux.h new file mode 100644 index 00000000000000..7c0a17c55c87e4 --- /dev/null +++ b/drivers/gpu/arm/ump/linux/ump_kernel_linux.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __UMP_KERNEL_LINUX_H__ +#define __UMP_KERNEL_LINUX_H__ + +int ump_kernel_device_initialize(void); +void ump_kernel_device_terminate(void); + + +#endif /* __UMP_KERNEL_H__ */ diff --git a/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_dedicated.c b/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_dedicated.c new file mode 100644 index 00000000000000..ca276c844de830 --- /dev/null +++ b/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_dedicated.c @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2010-2011, 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* needed to detect kernel version specific code */ +#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +#include +#else /* pre 2.6.26 the file was in the arch specific location */ +#include +#endif + +#include +#include +#include +#include +#include "ump_kernel_common.h" +#include "ump_kernel_memory_backend.h" + + + +#define UMP_BLOCK_SIZE (256UL * 1024UL) /* 256kB, remember to keep the ()s */ + + + +typedef struct block_info { + struct block_info * next; +} block_info; + + + +typedef struct block_allocator { + struct semaphore mutex; + block_info * all_blocks; + block_info * first_free; + u32 base; + u32 num_blocks; + u32 num_free; +} block_allocator; + + +static void block_allocator_shutdown(ump_memory_backend * backend); +static int block_allocator_allocate(void* ctx, ump_dd_mem * mem); +static void block_allocator_release(void * ctx, ump_dd_mem * handle); +static inline u32 get_phys(block_allocator * allocator, block_info * block); +static u32 block_allocator_stat(struct ump_memory_backend *backend); + + + +/* + * Create dedicated memory backend + */ +ump_memory_backend * ump_block_allocator_create(u32 base_address, u32 size) +{ + ump_memory_backend * backend; + block_allocator * allocator; + u32 usable_size; + u32 num_blocks; + + usable_size = (size + UMP_BLOCK_SIZE - 1) & ~(UMP_BLOCK_SIZE - 1); + num_blocks = usable_size / UMP_BLOCK_SIZE; + + if (0 == usable_size) { + DBG_MSG(1, ("Memory block of size %u is unusable\n", size)); + return NULL; + } + + DBG_MSG(5, ("Creating dedicated UMP memory backend. Base address: 0x%08x, size: 0x%08x\n", base_address, size)); + DBG_MSG(6, ("%u usable bytes which becomes %u blocks\n", usable_size, num_blocks)); + + backend = kzalloc(sizeof(ump_memory_backend), GFP_KERNEL); + if (NULL != backend) { + allocator = kmalloc(sizeof(block_allocator), GFP_KERNEL); + if (NULL != allocator) { + allocator->all_blocks = kmalloc(sizeof(block_info) * num_blocks, GFP_KERNEL); + if (NULL != allocator->all_blocks) { + int i; + + allocator->first_free = NULL; + allocator->num_blocks = num_blocks; + allocator->num_free = num_blocks; + allocator->base = base_address; + sema_init(&allocator->mutex, 1); + + for (i = 0; i < num_blocks; i++) { + allocator->all_blocks[i].next = allocator->first_free; + allocator->first_free = &allocator->all_blocks[i]; + } + + backend->ctx = allocator; + backend->allocate = block_allocator_allocate; + backend->release = block_allocator_release; + backend->shutdown = block_allocator_shutdown; + backend->stat = block_allocator_stat; + backend->pre_allocate_physical_check = NULL; + backend->adjust_to_mali_phys = NULL; + + return backend; + } + kfree(allocator); + } + kfree(backend); + } + + return NULL; +} + + + +/* + * Destroy specified dedicated memory backend + */ +static void block_allocator_shutdown(ump_memory_backend * backend) +{ + block_allocator * allocator; + + BUG_ON(!backend); + BUG_ON(!backend->ctx); + + allocator = (block_allocator*)backend->ctx; + + DBG_MSG_IF(1, allocator->num_free != allocator->num_blocks, ("%u blocks still in use during shutdown\n", allocator->num_blocks - allocator->num_free)); + + kfree(allocator->all_blocks); + kfree(allocator); + kfree(backend); +} + + + +static int block_allocator_allocate(void* ctx, ump_dd_mem * mem) +{ + block_allocator * allocator; + u32 left; + block_info * last_allocated = NULL; + int i = 0; + + BUG_ON(!ctx); + BUG_ON(!mem); + + allocator = (block_allocator*)ctx; + left = mem->size_bytes; + + BUG_ON(!left); + BUG_ON(!&allocator->mutex); + + mem->nr_blocks = ((left + UMP_BLOCK_SIZE - 1) & ~(UMP_BLOCK_SIZE - 1)) / UMP_BLOCK_SIZE; + mem->block_array = (ump_dd_physical_block*)vmalloc(sizeof(ump_dd_physical_block) * mem->nr_blocks); + if (NULL == mem->block_array) { + MSG_ERR(("Failed to allocate block array\n")); + return 0; + } + + if (down_interruptible(&allocator->mutex)) { + MSG_ERR(("Could not get mutex to do block_allocate\n")); + return 0; + } + + mem->size_bytes = 0; + + while ((left > 0) && (allocator->first_free)) { + block_info * block; + + block = allocator->first_free; + allocator->first_free = allocator->first_free->next; + block->next = last_allocated; + last_allocated = block; + allocator->num_free--; + + mem->block_array[i].addr = get_phys(allocator, block); + mem->block_array[i].size = UMP_BLOCK_SIZE; + mem->size_bytes += UMP_BLOCK_SIZE; + + i++; + + if (left < UMP_BLOCK_SIZE) left = 0; + else left -= UMP_BLOCK_SIZE; + } + + if (left) { + block_info * block; + /* release all memory back to the pool */ + while (last_allocated) { + block = last_allocated->next; + last_allocated->next = allocator->first_free; + allocator->first_free = last_allocated; + last_allocated = block; + allocator->num_free++; + } + + vfree(mem->block_array); + mem->backend_info = NULL; + mem->block_array = NULL; + + DBG_MSG(4, ("Could not find a mem-block for the allocation.\n")); + up(&allocator->mutex); + + return 0; + } + + mem->backend_info = last_allocated; + + up(&allocator->mutex); + mem->is_cached=0; + + return 1; +} + + + +static void block_allocator_release(void * ctx, ump_dd_mem * handle) +{ + block_allocator * allocator; + block_info * block, * next; + + BUG_ON(!ctx); + BUG_ON(!handle); + + allocator = (block_allocator*)ctx; + block = (block_info*)handle->backend_info; + BUG_ON(!block); + + if (down_interruptible(&allocator->mutex)) { + MSG_ERR(("Allocator release: Failed to get mutex - memory leak\n")); + return; + } + + while (block) { + next = block->next; + + BUG_ON( (block < allocator->all_blocks) || (block > (allocator->all_blocks + allocator->num_blocks))); + + block->next = allocator->first_free; + allocator->first_free = block; + allocator->num_free++; + + block = next; + } + DBG_MSG(3, ("%d blocks free after release call\n", allocator->num_free)); + up(&allocator->mutex); + + vfree(handle->block_array); + handle->block_array = NULL; +} + + + +/* + * Helper function for calculating the physical base adderss of a memory block + */ +static inline u32 get_phys(block_allocator * allocator, block_info * block) +{ + return allocator->base + ((block - allocator->all_blocks) * UMP_BLOCK_SIZE); +} + +static u32 block_allocator_stat(struct ump_memory_backend *backend) +{ + block_allocator *allocator; + BUG_ON(!backend); + allocator = (block_allocator*)backend->ctx; + BUG_ON(!allocator); + + return (allocator->num_blocks - allocator->num_free)* UMP_BLOCK_SIZE; +} diff --git a/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_dedicated.h b/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_dedicated.h new file mode 100644 index 00000000000000..fa4bdccfe32cd2 --- /dev/null +++ b/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_dedicated.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2010 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_memory_backend_dedicated.h + */ + +#ifndef __UMP_KERNEL_MEMORY_BACKEND_DEDICATED_H__ +#define __UMP_KERNEL_MEMORY_BACKEND_DEDICATED_H__ + +#include "ump_kernel_memory_backend.h" + +ump_memory_backend * ump_block_allocator_create(u32 base_address, u32 size); + +#endif /* __UMP_KERNEL_MEMORY_BACKEND_DEDICATED_H__ */ + diff --git a/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_os.c b/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_os.c new file mode 100644 index 00000000000000..61817c74ac8e13 --- /dev/null +++ b/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_os.c @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2010-2011, 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* needed to detect kernel version specific code */ +#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +#include +#else /* pre 2.6.26 the file was in the arch specific location */ +#include +#endif + +#include +#include +#include +#include +#include +#include +#include "ump_kernel_common.h" +#include "ump_kernel_memory_backend.h" + + + +typedef struct os_allocator { + struct semaphore mutex; + u32 num_pages_max; /**< Maximum number of pages to allocate from the OS */ + u32 num_pages_allocated; /**< Number of pages allocated from the OS */ +} os_allocator; + + + +static void os_free(void* ctx, ump_dd_mem * descriptor); +static int os_allocate(void* ctx, ump_dd_mem * descriptor); +static void os_memory_backend_destroy(ump_memory_backend * backend); +static u32 os_stat(struct ump_memory_backend *backend); + + + +/* + * Create OS memory backend + */ +ump_memory_backend * ump_os_memory_backend_create(const int max_allocation) +{ + ump_memory_backend * backend; + os_allocator * info; + + info = kmalloc(sizeof(os_allocator), GFP_KERNEL); + if (NULL == info) { + return NULL; + } + + info->num_pages_max = max_allocation >> PAGE_SHIFT; + info->num_pages_allocated = 0; + + sema_init(&info->mutex, 1); + + backend = kmalloc(sizeof(ump_memory_backend), GFP_KERNEL); + if (NULL == backend) { + kfree(info); + return NULL; + } + + backend->ctx = info; + backend->allocate = os_allocate; + backend->release = os_free; + backend->shutdown = os_memory_backend_destroy; + backend->stat = os_stat; + backend->pre_allocate_physical_check = NULL; + backend->adjust_to_mali_phys = NULL; + + return backend; +} + + + +/* + * Destroy specified OS memory backend + */ +static void os_memory_backend_destroy(ump_memory_backend * backend) +{ + os_allocator * info = (os_allocator*)backend->ctx; + + DBG_MSG_IF(1, 0 != info->num_pages_allocated, ("%d pages still in use during shutdown\n", info->num_pages_allocated)); + + kfree(info); + kfree(backend); +} + + + +/* + * Allocate UMP memory + */ +static int os_allocate(void* ctx, ump_dd_mem * descriptor) +{ + u32 left; + os_allocator * info; + int pages_allocated = 0; + int is_cached; + + BUG_ON(!descriptor); + BUG_ON(!ctx); + + info = (os_allocator*)ctx; + left = descriptor->size_bytes; + is_cached = descriptor->is_cached; + + if (down_interruptible(&info->mutex)) { + DBG_MSG(1, ("Failed to get mutex in os_free\n")); + return 0; /* failure */ + } + + descriptor->backend_info = NULL; + descriptor->nr_blocks = ((left + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) >> PAGE_SHIFT; + + DBG_MSG(5, ("Allocating page array. Size: %lu\n", descriptor->nr_blocks * sizeof(ump_dd_physical_block))); + + descriptor->block_array = (ump_dd_physical_block *)vmalloc(sizeof(ump_dd_physical_block) * descriptor->nr_blocks); + if (NULL == descriptor->block_array) { + up(&info->mutex); + DBG_MSG(1, ("Block array could not be allocated\n")); + return 0; /* failure */ + } + + while (left > 0 && ((info->num_pages_allocated + pages_allocated) < info->num_pages_max)) { + struct page * new_page; + + if (is_cached) { + new_page = alloc_page(GFP_HIGHUSER | __GFP_ZERO | __GFP_REPEAT | __GFP_NOWARN); + } else { + new_page = alloc_page(GFP_HIGHUSER | __GFP_ZERO | __GFP_REPEAT | __GFP_NOWARN | __GFP_COLD); + } + if (NULL == new_page) { + break; + } + + /* Ensure page caches are flushed. */ + if ( is_cached ) { + descriptor->block_array[pages_allocated].addr = page_to_phys(new_page); + descriptor->block_array[pages_allocated].size = PAGE_SIZE; + } else { + descriptor->block_array[pages_allocated].addr = dma_map_page(NULL, new_page, 0, PAGE_SIZE, DMA_BIDIRECTIONAL ); + descriptor->block_array[pages_allocated].size = PAGE_SIZE; + } + + DBG_MSG(5, ("Allocated page 0x%08lx cached: %d\n", descriptor->block_array[pages_allocated].addr, is_cached)); + + if (left < PAGE_SIZE) { + left = 0; + } else { + left -= PAGE_SIZE; + } + + pages_allocated++; + } + + DBG_MSG(5, ("Alloce for ID:%2d got %d pages, cached: %d\n", descriptor->secure_id, pages_allocated)); + + if (left) { + DBG_MSG(1, ("Failed to allocate needed pages\n")); + + while(pages_allocated) { + pages_allocated--; + if ( !is_cached ) { + dma_unmap_page(NULL, descriptor->block_array[pages_allocated].addr, PAGE_SIZE, DMA_BIDIRECTIONAL); + } + __free_page(pfn_to_page(descriptor->block_array[pages_allocated].addr >> PAGE_SHIFT) ); + } + + up(&info->mutex); + + return 0; /* failure */ + } + + info->num_pages_allocated += pages_allocated; + + DBG_MSG(6, ("%d out of %d pages now allocated\n", info->num_pages_allocated, info->num_pages_max)); + + up(&info->mutex); + + return 1; /* success*/ +} + + +/* + * Free specified UMP memory + */ +static void os_free(void* ctx, ump_dd_mem * descriptor) +{ + os_allocator * info; + int i; + + BUG_ON(!ctx); + BUG_ON(!descriptor); + + info = (os_allocator*)ctx; + + BUG_ON(descriptor->nr_blocks > info->num_pages_allocated); + + if (down_interruptible(&info->mutex)) { + DBG_MSG(1, ("Failed to get mutex in os_free\n")); + return; + } + + DBG_MSG(5, ("Releasing %lu OS pages\n", descriptor->nr_blocks)); + + info->num_pages_allocated -= descriptor->nr_blocks; + + up(&info->mutex); + + for ( i = 0; i < descriptor->nr_blocks; i++) { + DBG_MSG(6, ("Freeing physical page. Address: 0x%08lx\n", descriptor->block_array[i].addr)); + if ( ! descriptor->is_cached) { + dma_unmap_page(NULL, descriptor->block_array[i].addr, PAGE_SIZE, DMA_BIDIRECTIONAL); + } + __free_page(pfn_to_page(descriptor->block_array[i].addr>>PAGE_SHIFT) ); + } + + vfree(descriptor->block_array); +} + + +static u32 os_stat(struct ump_memory_backend *backend) +{ + os_allocator *info; + info = (os_allocator*)backend->ctx; + return info->num_pages_allocated * _MALI_OSK_MALI_PAGE_SIZE; +} diff --git a/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_os.h b/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_os.h new file mode 100644 index 00000000000000..f924705cf742fd --- /dev/null +++ b/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_os.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2010 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_memory_backend_os.h + */ + +#ifndef __UMP_KERNEL_MEMORY_BACKEND_OS_H__ +#define __UMP_KERNEL_MEMORY_BACKEND_OS_H__ + +#include "ump_kernel_memory_backend.h" + +ump_memory_backend * ump_os_memory_backend_create(const int max_allocation); + +#endif /* __UMP_KERNEL_MEMORY_BACKEND_OS_H__ */ + diff --git a/drivers/gpu/arm/ump/linux/ump_memory_backend.c b/drivers/gpu/arm/ump/linux/ump_memory_backend.c new file mode 100644 index 00000000000000..e7f84dbbcf1daa --- /dev/null +++ b/drivers/gpu/arm/ump/linux/ump_memory_backend.c @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2010, 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include /* kernel module definitions */ +#include /* request_mem_region */ + +#include "arch/config.h" /* Configuration for current platform. The symlink for arch is set by Makefile */ + +#include "ump_osk.h" +#include "ump_kernel_common.h" +#include "ump_kernel_memory_backend_os.h" +#include "ump_kernel_memory_backend_dedicated.h" + +/* Configure which dynamic memory allocator to use */ +int ump_backend = ARCH_UMP_BACKEND_DEFAULT; +module_param(ump_backend, int, S_IRUGO); /* r--r--r-- */ +MODULE_PARM_DESC(ump_backend, "0 = dedicated memory backend (default), 1 = OS memory backend"); + +/* The base address of the memory block for the dedicated memory backend */ +unsigned int ump_memory_address = ARCH_UMP_MEMORY_ADDRESS_DEFAULT; +module_param(ump_memory_address, uint, S_IRUGO); /* r--r--r-- */ +MODULE_PARM_DESC(ump_memory_address, "The physical address to map for the dedicated memory backend"); + +/* The size of the memory block for the dedicated memory backend */ +unsigned int ump_memory_size = ARCH_UMP_MEMORY_SIZE_DEFAULT; +module_param(ump_memory_size, uint, S_IRUGO); /* r--r--r-- */ +MODULE_PARM_DESC(ump_memory_size, "The size of fixed memory to map in the dedicated memory backend"); + +ump_memory_backend* ump_memory_backend_create ( void ) +{ + ump_memory_backend * backend = NULL; + + /* Create the dynamic memory allocator backend */ + if (0 == ump_backend) { + DBG_MSG(2, ("Using dedicated memory backend\n")); + + DBG_MSG(2, ("Requesting dedicated memory: 0x%08x, size: %u\n", ump_memory_address, ump_memory_size)); + /* Ask the OS if we can use the specified physical memory */ + if (NULL == request_mem_region(ump_memory_address, ump_memory_size, "UMP Memory")) { + MSG_ERR(("Failed to request memory region (0x%08X - 0x%08X). Is Mali DD already loaded?\n", ump_memory_address, ump_memory_address + ump_memory_size - 1)); + return NULL; + } + backend = ump_block_allocator_create(ump_memory_address, ump_memory_size); + } else if (1 == ump_backend) { + DBG_MSG(2, ("Using OS memory backend, allocation limit: %d\n", ump_memory_size)); + backend = ump_os_memory_backend_create(ump_memory_size); + } + + return backend; +} + +void ump_memory_backend_destroy( void ) +{ + if (0 == ump_backend) { + DBG_MSG(2, ("Releasing dedicated memory: 0x%08x\n", ump_memory_address)); + release_mem_region(ump_memory_address, ump_memory_size); + } +} diff --git a/drivers/gpu/arm/ump/linux/ump_osk_atomics.c b/drivers/gpu/arm/ump/linux/ump_osk_atomics.c new file mode 100644 index 00000000000000..ef1902e5391b2b --- /dev/null +++ b/drivers/gpu/arm/ump/linux/ump_osk_atomics.c @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2010 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_osk_atomics.c + * Implementation of the OS abstraction layer for the UMP kernel device driver + */ + +#include "ump_osk.h" +#include + +int _ump_osk_atomic_dec_and_read( _mali_osk_atomic_t *atom ) +{ + return atomic_dec_return((atomic_t *)&atom->u.val); +} + +int _ump_osk_atomic_inc_and_read( _mali_osk_atomic_t *atom ) +{ + return atomic_inc_return((atomic_t *)&atom->u.val); +} diff --git a/drivers/gpu/arm/ump/linux/ump_osk_low_level_mem.c b/drivers/gpu/arm/ump/linux/ump_osk_low_level_mem.c new file mode 100644 index 00000000000000..0736c93331aa01 --- /dev/null +++ b/drivers/gpu/arm/ump/linux/ump_osk_low_level_mem.c @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_osk_memory.c + * Implementation of the OS abstraction layer for the kernel device driver + */ + +/* needed to detect kernel version specific code */ +#include + +#include "ump_osk.h" +#include "ump_uk_types.h" +#include "ump_ukk.h" +#include "ump_kernel_common.h" +#include /* kernel module definitions */ +#include +#include +#include + +#include +#include /* to verify pointers from user space */ +#include +#include + +typedef struct ump_vma_usage_tracker { + atomic_t references; + ump_memory_allocation *descriptor; +} ump_vma_usage_tracker; + +static void ump_vma_open(struct vm_area_struct * vma); +static void ump_vma_close(struct vm_area_struct * vma); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +static int ump_cpu_page_fault_handler(struct vm_area_struct *vma, struct vm_fault *vmf); +#else +static unsigned long ump_cpu_page_fault_handler(struct vm_area_struct * vma, unsigned long address); +#endif + +static struct vm_operations_struct ump_vm_ops = { + .open = ump_vma_open, + .close = ump_vma_close, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) + .fault = ump_cpu_page_fault_handler +#else + .nopfn = ump_cpu_page_fault_handler +#endif +}; + +/* + * Page fault for VMA region + * This should never happen since we always map in the entire virtual memory range. + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +static int ump_cpu_page_fault_handler(struct vm_area_struct *vma, struct vm_fault *vmf) +#else +static unsigned long ump_cpu_page_fault_handler(struct vm_area_struct * vma, unsigned long address) +#endif +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) + void __user * address; + address = vmf->virtual_address; +#endif + MSG_ERR(("Page-fault in UMP memory region caused by the CPU\n")); + MSG_ERR(("VMA: 0x%08lx, virtual address: 0x%08lx\n", (unsigned long)vma, address)); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) + return VM_FAULT_SIGBUS; +#else + return NOPFN_SIGBUS; +#endif +} + +static void ump_vma_open(struct vm_area_struct * vma) +{ + ump_vma_usage_tracker * vma_usage_tracker; + int new_val; + + vma_usage_tracker = (ump_vma_usage_tracker*)vma->vm_private_data; + BUG_ON(NULL == vma_usage_tracker); + + new_val = atomic_inc_return(&vma_usage_tracker->references); + + DBG_MSG(4, ("VMA open, VMA reference count incremented. VMA: 0x%08lx, reference count: %d\n", (unsigned long)vma, new_val)); +} + +static void ump_vma_close(struct vm_area_struct * vma) +{ + ump_vma_usage_tracker * vma_usage_tracker; + _ump_uk_unmap_mem_s args; + int new_val; + + vma_usage_tracker = (ump_vma_usage_tracker*)vma->vm_private_data; + BUG_ON(NULL == vma_usage_tracker); + + new_val = atomic_dec_return(&vma_usage_tracker->references); + + DBG_MSG(4, ("VMA close, VMA reference count decremented. VMA: 0x%08lx, reference count: %d\n", (unsigned long)vma, new_val)); + + if (0 == new_val) { + ump_memory_allocation * descriptor; + + descriptor = vma_usage_tracker->descriptor; + + args.ctx = descriptor->ump_session; + args.cookie = descriptor->cookie; + args.mapping = descriptor->mapping; + args.size = descriptor->size; + + args._ukk_private = NULL; /** @note unused */ + + DBG_MSG(4, ("No more VMA references left, releasing UMP memory\n")); + _ump_ukk_unmap_mem( & args ); + + /* vma_usage_tracker is free()d by _ump_osk_mem_mapregion_term() */ + } +} + +_mali_osk_errcode_t _ump_osk_mem_mapregion_init( ump_memory_allocation * descriptor ) +{ + ump_vma_usage_tracker * vma_usage_tracker; + struct vm_area_struct *vma; + + if (NULL == descriptor) return _MALI_OSK_ERR_FAULT; + + vma_usage_tracker = kmalloc(sizeof(ump_vma_usage_tracker), GFP_KERNEL); + if (NULL == vma_usage_tracker) { + DBG_MSG(1, ("Failed to allocate memory for ump_vma_usage_tracker in _mali_osk_mem_mapregion_init\n")); + return -_MALI_OSK_ERR_FAULT; + } + + vma = (struct vm_area_struct*)descriptor->process_mapping_info; + if (NULL == vma ) { + kfree(vma_usage_tracker); + return _MALI_OSK_ERR_FAULT; + } + + vma->vm_private_data = vma_usage_tracker; + vma->vm_flags |= VM_IO; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) + vma->vm_flags |= VM_RESERVED; +#else + vma->vm_flags |= VM_DONTDUMP; + vma->vm_flags |= VM_DONTEXPAND; + vma->vm_flags |= VM_PFNMAP; +#endif + + + if (0==descriptor->is_cached) { + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + } + DBG_MSG(3, ("Mapping with page_prot: 0x%x\n", vma->vm_page_prot )); + + /* Setup the functions which handle further VMA handling */ + vma->vm_ops = &ump_vm_ops; + + /* Do the va range allocation - in this case, it was done earlier, so we copy in that information */ + descriptor->mapping = (void __user*)vma->vm_start; + + atomic_set(&vma_usage_tracker->references, 1); /*this can later be increased if process is forked, see ump_vma_open() */ + vma_usage_tracker->descriptor = descriptor; + + return _MALI_OSK_ERR_OK; +} + +void _ump_osk_mem_mapregion_term( ump_memory_allocation * descriptor ) +{ + struct vm_area_struct* vma; + ump_vma_usage_tracker * vma_usage_tracker; + + if (NULL == descriptor) return; + + /* Linux does the right thing as part of munmap to remove the mapping + * All that remains is that we remove the vma_usage_tracker setup in init() */ + vma = (struct vm_area_struct*)descriptor->process_mapping_info; + + vma_usage_tracker = vma->vm_private_data; + + /* We only get called if mem_mapregion_init succeeded */ + kfree(vma_usage_tracker); + return; +} + +_mali_osk_errcode_t _ump_osk_mem_mapregion_map( ump_memory_allocation * descriptor, u32 offset, u32 * phys_addr, unsigned long size ) +{ + struct vm_area_struct *vma; + _mali_osk_errcode_t retval; + + if (NULL == descriptor) return _MALI_OSK_ERR_FAULT; + + vma = (struct vm_area_struct*)descriptor->process_mapping_info; + + if (NULL == vma ) return _MALI_OSK_ERR_FAULT; + + retval = remap_pfn_range( vma, ((u32)descriptor->mapping) + offset, (*phys_addr) >> PAGE_SHIFT, size, vma->vm_page_prot) ? _MALI_OSK_ERR_FAULT : _MALI_OSK_ERR_OK;; + + DBG_MSG(4, ("Mapping virtual to physical memory. ID: %u, vma: 0x%08lx, virtual addr:0x%08lx, physical addr: 0x%08lx, size:%lu, prot:0x%x, vm_flags:0x%x RETVAL: 0x%x\n", + ump_dd_secure_id_get(descriptor->handle), + (unsigned long)vma, + (unsigned long)(vma->vm_start + offset), + (unsigned long)*phys_addr, + size, + (unsigned int)vma->vm_page_prot, vma->vm_flags, retval)); + + return retval; +} + +static void level1_cache_flush_all(void) +{ + DBG_MSG(4, ("UMP[xx] Flushing complete L1 cache\n")); + __cpuc_flush_kern_all(); +} + +void _ump_osk_msync( ump_dd_mem * mem, void * virt, u32 offset, u32 size, ump_uk_msync_op op, ump_session_data * session_data ) +{ + int i; + + /* Flush L1 using virtual address, the entire range in one go. + * Only flush if user space process has a valid write mapping on given address. */ + if( (mem) && (virt!=NULL) && (access_ok(VERIFY_WRITE, virt, size)) ) { + __cpuc_flush_dcache_area(virt, size); + DBG_MSG(3, ("UMP[%02u] Flushing CPU L1 Cache. CPU address: %x, size: %x\n", mem->secure_id, virt, size)); + } else { + if (session_data) { + if (op == _UMP_UK_MSYNC_FLUSH_L1 ) { + DBG_MSG(4, ("UMP Pending L1 cache flushes: %d\n", session_data->has_pending_level1_cache_flush)); + session_data->has_pending_level1_cache_flush = 0; + level1_cache_flush_all(); + return; + } else { + if (session_data->cache_operations_ongoing) { + session_data->has_pending_level1_cache_flush++; + DBG_MSG(4, ("UMP[%02u] Defering the L1 flush. Nr pending:%d\n", mem->secure_id, session_data->has_pending_level1_cache_flush) ); + } else { + /* Flushing the L1 cache for each switch_user() if ump_cache_operations_control(START) is not called */ + level1_cache_flush_all(); + } + } + } else { + DBG_MSG(4, ("Unkown state %s %d\n", __FUNCTION__, __LINE__)); + level1_cache_flush_all(); + } + } + + if ( NULL == mem ) return; + + if ( mem->size_bytes==size) { + DBG_MSG(3, ("UMP[%02u] Flushing CPU L2 Cache\n",mem->secure_id)); + } else { + DBG_MSG(3, ("UMP[%02u] Flushing CPU L2 Cache. Blocks:%u, TotalSize:%u. FlushSize:%u Offset:0x%x FirstPaddr:0x%08x\n", + mem->secure_id, mem->nr_blocks, mem->size_bytes, size, offset, mem->block_array[0].addr)); + } + + + /* Flush L2 using physical addresses, block for block. */ + for (i=0 ; i < mem->nr_blocks; i++) { + u32 start_p, end_p; + ump_dd_physical_block *block; + block = &mem->block_array[i]; + + if(offset >= block->size) { + offset -= block->size; + continue; + } + + if(offset) { + start_p = (u32)block->addr + offset; + /* We'll zero the offset later, after using it to calculate end_p. */ + } else { + start_p = (u32)block->addr; + } + + if(size < block->size - offset) { + end_p = start_p + size - 1; + size = 0; + } else { + if(offset) { + end_p = start_p + (block->size - offset - 1); + size -= block->size - offset; + offset = 0; + } else { + end_p = start_p + block->size - 1; + size -= block->size; + } + } + + switch(op) { + case _UMP_UK_MSYNC_CLEAN: + outer_clean_range(start_p, end_p); + break; + case _UMP_UK_MSYNC_CLEAN_AND_INVALIDATE: + outer_flush_range(start_p, end_p); + break; + case _UMP_UK_MSYNC_INVALIDATE: + outer_inv_range(start_p, end_p); + break; + default: + break; + } + + if(0 == size) { + /* Nothing left to flush. */ + break; + } + } + + return; +} diff --git a/drivers/gpu/arm/ump/linux/ump_osk_misc.c b/drivers/gpu/arm/ump/linux/ump_osk_misc.c new file mode 100644 index 00000000000000..ddbce844cd7d28 --- /dev/null +++ b/drivers/gpu/arm/ump/linux/ump_osk_misc.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2010, 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_osk_misc.c + * Implementation of the OS abstraction layer for the UMP kernel device driver + */ + + +#include "ump_osk.h" + +#include +#include "ump_kernel_linux.h" + +/* is called from ump_kernel_constructor in common code */ +_mali_osk_errcode_t _ump_osk_init( void ) +{ + if (0 != ump_kernel_device_initialize()) { + return _MALI_OSK_ERR_FAULT; + } + + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t _ump_osk_term( void ) +{ + ump_kernel_device_terminate(); + return _MALI_OSK_ERR_OK; +} diff --git a/drivers/gpu/arm/ump/linux/ump_ukk_ref_wrappers.c b/drivers/gpu/arm/ump/linux/ump_ukk_ref_wrappers.c new file mode 100644 index 00000000000000..8cbe538e27b7c9 --- /dev/null +++ b/drivers/gpu/arm/ump/linux/ump_ukk_ref_wrappers.c @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2010, 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_ukk_wrappers.c + * Defines the wrapper functions which turn Linux IOCTL calls into _ukk_ calls for the reference implementation + */ + + +#include /* user space access */ + +#include "ump_osk.h" +#include "ump_uk_types.h" +#include "ump_ukk.h" +#include "ump_kernel_common.h" + +/* + * IOCTL operation; Allocate UMP memory + */ +int ump_allocate_wrapper(u32 __user * argument, struct ump_session_data * session_data) +{ + _ump_uk_allocate_s user_interaction; + _mali_osk_errcode_t err; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) { + MSG_ERR(("NULL parameter in ump_ioctl_allocate()\n")); + return -ENOTTY; + } + + /* Copy the user space memory to kernel space (so we safely can read it) */ + if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) { + MSG_ERR(("copy_from_user() in ump_ioctl_allocate()\n")); + return -EFAULT; + } + + user_interaction.ctx = (void *) session_data; + + err = _ump_ukk_allocate( &user_interaction ); + if( _MALI_OSK_ERR_OK != err ) { + DBG_MSG(1, ("_ump_ukk_allocate() failed in ump_ioctl_allocate()\n")); + return map_errcode(err); + } + user_interaction.ctx = NULL; + + if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) { + /* If the copy fails then we should release the memory. We can use the IOCTL release to accomplish this */ + _ump_uk_release_s release_args; + + MSG_ERR(("copy_to_user() failed in ump_ioctl_allocate()\n")); + + release_args.ctx = (void *) session_data; + release_args.secure_id = user_interaction.secure_id; + + err = _ump_ukk_release( &release_args ); + if(_MALI_OSK_ERR_OK != err) { + MSG_ERR(("_ump_ukk_release() also failed when trying to release newly allocated memory in ump_ioctl_allocate()\n")); + } + + return -EFAULT; + } + + return 0; /* success */ +} diff --git a/drivers/gpu/arm/ump/linux/ump_ukk_ref_wrappers.h b/drivers/gpu/arm/ump/linux/ump_ukk_ref_wrappers.h new file mode 100644 index 00000000000000..43dabd4d368361 --- /dev/null +++ b/drivers/gpu/arm/ump/linux/ump_ukk_ref_wrappers.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2010, 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_ukk_wrappers.h + * Defines the wrapper functions which turn Linux IOCTL calls into _ukk_ calls for the reference implementation + */ + +#ifndef __UMP_UKK_REF_WRAPPERS_H__ +#define __UMP_UKK_REF_WRAPPERS_H__ + +#include +#include "ump_kernel_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +int ump_allocate_wrapper(u32 __user * argument, struct ump_session_data * session_data); + + +#ifdef __cplusplus +} +#endif + +#endif /* __UMP_UKK_REF_WRAPPERS_H__ */ diff --git a/drivers/gpu/arm/ump/linux/ump_ukk_wrappers.c b/drivers/gpu/arm/ump/linux/ump_ukk_wrappers.c new file mode 100644 index 00000000000000..6c1831eefe5275 --- /dev/null +++ b/drivers/gpu/arm/ump/linux/ump_ukk_wrappers.c @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2010-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_ukk_wrappers.c + * Defines the wrapper functions which turn Linux IOCTL calls into _ukk_ calls + */ + +#include /* user space access */ + +#include "ump_osk.h" +#include "ump_uk_types.h" +#include "ump_ukk.h" +#include "ump_kernel_common.h" + +/* + * IOCTL operation; Negotiate version of IOCTL API + */ +int ump_get_api_version_wrapper(u32 __user * argument, struct ump_session_data * session_data) +{ + _ump_uk_api_version_s version_info; + _mali_osk_errcode_t err; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) { + MSG_ERR(("NULL parameter in ump_ioctl_get_api_version()\n")); + return -ENOTTY; + } + + /* Copy the user space memory to kernel space (so we safely can read it) */ + if (0 != copy_from_user(&version_info, argument, sizeof(version_info))) { + MSG_ERR(("copy_from_user() in ump_ioctl_get_api_version()\n")); + return -EFAULT; + } + + version_info.ctx = (void*) session_data; + err = _ump_uku_get_api_version( &version_info ); + if( _MALI_OSK_ERR_OK != err ) { + MSG_ERR(("_ump_uku_get_api_version() failed in ump_ioctl_get_api_version()\n")); + return map_errcode(err); + } + + version_info.ctx = NULL; + + /* Copy ouput data back to user space */ + if (0 != copy_to_user(argument, &version_info, sizeof(version_info))) { + MSG_ERR(("copy_to_user() failed in ump_ioctl_get_api_version()\n")); + return -EFAULT; + } + + return 0; /* success */ +} + + +/* + * IOCTL operation; Release reference to specified UMP memory. + */ +int ump_release_wrapper(u32 __user * argument, struct ump_session_data * session_data) +{ + _ump_uk_release_s release_args; + _mali_osk_errcode_t err; + + /* Sanity check input parameters */ + if (NULL == session_data) { + MSG_ERR(("NULL parameter in ump_ioctl_release()\n")); + return -ENOTTY; + } + + /* Copy the user space memory to kernel space (so we safely can read it) */ + if (0 != copy_from_user(&release_args, argument, sizeof(release_args))) { + MSG_ERR(("copy_from_user() in ump_ioctl_get_api_version()\n")); + return -EFAULT; + } + + release_args.ctx = (void*) session_data; + err = _ump_ukk_release( &release_args ); + if( _MALI_OSK_ERR_OK != err ) { + MSG_ERR(("_ump_ukk_release() failed in ump_ioctl_release()\n")); + return map_errcode(err); + } + + + return 0; /* success */ +} + +/* + * IOCTL operation; Return size for specified UMP memory. + */ +int ump_size_get_wrapper(u32 __user * argument, struct ump_session_data * session_data) +{ + _ump_uk_size_get_s user_interaction; + _mali_osk_errcode_t err; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) { + MSG_ERR(("NULL parameter in ump_ioctl_size_get()\n")); + return -ENOTTY; + } + + if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) { + MSG_ERR(("copy_from_user() in ump_ioctl_size_get()\n")); + return -EFAULT; + } + + user_interaction.ctx = (void *) session_data; + err = _ump_ukk_size_get( &user_interaction ); + if( _MALI_OSK_ERR_OK != err ) { + MSG_ERR(("_ump_ukk_size_get() failed in ump_ioctl_size_get()\n")); + return map_errcode(err); + } + + user_interaction.ctx = NULL; + + if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) { + MSG_ERR(("copy_to_user() failed in ump_ioctl_size_get()\n")); + return -EFAULT; + } + + return 0; /* success */ +} + +/* + * IOCTL operation; Do cache maintenance on specified UMP memory. + */ +int ump_msync_wrapper(u32 __user * argument, struct ump_session_data * session_data) +{ + _ump_uk_msync_s user_interaction; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) { + MSG_ERR(("NULL parameter in ump_ioctl_size_get()\n")); + return -ENOTTY; + } + + if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) { + MSG_ERR(("copy_from_user() in ump_ioctl_msync()\n")); + return -EFAULT; + } + + user_interaction.ctx = (void *) session_data; + + _ump_ukk_msync( &user_interaction ); + + user_interaction.ctx = NULL; + + if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) { + MSG_ERR(("copy_to_user() failed in ump_ioctl_msync()\n")); + return -EFAULT; + } + + return 0; /* success */ +} +int ump_cache_operations_control_wrapper(u32 __user * argument, struct ump_session_data * session_data) +{ + _ump_uk_cache_operations_control_s user_interaction; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) { + MSG_ERR(("NULL parameter in ump_ioctl_size_get()\n")); + return -ENOTTY; + } + + if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) { + MSG_ERR(("copy_from_user() in ump_ioctl_cache_operations_control()\n")); + return -EFAULT; + } + + user_interaction.ctx = (void *) session_data; + + _ump_ukk_cache_operations_control((_ump_uk_cache_operations_control_s*) &user_interaction ); + + user_interaction.ctx = NULL; + +#if 0 /* No data to copy back */ + if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) { + MSG_ERR(("copy_to_user() failed in ump_ioctl_cache_operations_control()\n")); + return -EFAULT; + } +#endif + return 0; /* success */ +} + +int ump_switch_hw_usage_wrapper(u32 __user * argument, struct ump_session_data * session_data) +{ + _ump_uk_switch_hw_usage_s user_interaction; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) { + MSG_ERR(("NULL parameter in ump_ioctl_size_get()\n")); + return -ENOTTY; + } + + if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) { + MSG_ERR(("copy_from_user() in ump_ioctl_switch_hw_usage()\n")); + return -EFAULT; + } + + user_interaction.ctx = (void *) session_data; + + _ump_ukk_switch_hw_usage( &user_interaction ); + + user_interaction.ctx = NULL; + +#if 0 /* No data to copy back */ + if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) { + MSG_ERR(("copy_to_user() failed in ump_ioctl_switch_hw_usage()\n")); + return -EFAULT; + } +#endif + return 0; /* success */ +} + +int ump_lock_wrapper(u32 __user * argument, struct ump_session_data * session_data) +{ + _ump_uk_lock_s user_interaction; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) { + MSG_ERR(("NULL parameter in ump_ioctl_size_get()\n")); + return -ENOTTY; + } + + if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) { + MSG_ERR(("copy_from_user() in ump_ioctl_switch_hw_usage()\n")); + return -EFAULT; + } + + user_interaction.ctx = (void *) session_data; + + _ump_ukk_lock( &user_interaction ); + + user_interaction.ctx = NULL; + +#if 0 /* No data to copy back */ + if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) { + MSG_ERR(("copy_to_user() failed in ump_ioctl_switch_hw_usage()\n")); + return -EFAULT; + } +#endif + + return 0; /* success */ +} + +int ump_unlock_wrapper(u32 __user * argument, struct ump_session_data * session_data) +{ + _ump_uk_unlock_s user_interaction; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) { + MSG_ERR(("NULL parameter in ump_ioctl_size_get()\n")); + return -ENOTTY; + } + + if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) { + MSG_ERR(("copy_from_user() in ump_ioctl_switch_hw_usage()\n")); + return -EFAULT; + } + + user_interaction.ctx = (void *) session_data; + + _ump_ukk_unlock( &user_interaction ); + + user_interaction.ctx = NULL; + +#if 0 /* No data to copy back */ + if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) { + MSG_ERR(("copy_to_user() failed in ump_ioctl_switch_hw_usage()\n")); + return -EFAULT; + } +#endif + + return 0; /* success */ +} diff --git a/drivers/gpu/arm/ump/linux/ump_ukk_wrappers.h b/drivers/gpu/arm/ump/linux/ump_ukk_wrappers.h new file mode 100644 index 00000000000000..ba76e3dbaf9dc5 --- /dev/null +++ b/drivers/gpu/arm/ump/linux/ump_ukk_wrappers.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2010, 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_ukk_wrappers.h + * Defines the wrapper functions which turn Linux IOCTL calls into _ukk_ calls + */ + +#ifndef __UMP_UKK_WRAPPERS_H__ +#define __UMP_UKK_WRAPPERS_H__ + +#include +#include "ump_kernel_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + +int ump_get_api_version_wrapper(u32 __user * argument, struct ump_session_data * session_data); +int ump_release_wrapper(u32 __user * argument, struct ump_session_data * session_data); +int ump_size_get_wrapper(u32 __user * argument, struct ump_session_data * session_data); +int ump_msync_wrapper(u32 __user * argument, struct ump_session_data * session_data); +int ump_cache_operations_control_wrapper(u32 __user * argument, struct ump_session_data * session_data); +int ump_switch_hw_usage_wrapper(u32 __user * argument, struct ump_session_data * session_data); +int ump_lock_wrapper(u32 __user * argument, struct ump_session_data * session_data); +int ump_unlock_wrapper(u32 __user * argument, struct ump_session_data * session_data); + + + + +#ifdef __cplusplus +} +#endif + + + +#endif /* __UMP_UKK_WRAPPERS_H__ */ diff --git a/drivers/gpu/arm/ump/readme.txt b/drivers/gpu/arm/ump/readme.txt new file mode 100644 index 00000000000000..c238cf0f2b1f0b --- /dev/null +++ b/drivers/gpu/arm/ump/readme.txt @@ -0,0 +1,28 @@ +Building the UMP Device Driver for Linux +---------------------------------------- + +Build the UMP Device Driver for Linux by running the following make command: + +KDIR= CONFIG= BUILD= make + +where + kdir_path: Path to your Linux Kernel directory + your_config: Name of the sub-folder to find the required config.h file + ("arch-" will be prepended) + build_option: debug or release. Debug is default. + +The config.h contains following configuration parameters: + +ARCH_UMP_BACKEND_DEFAULT + 0 specifies the dedicated memory allocator. + 1 specifies the OS memory allocator. +ARCH_UMP_MEMORY_ADDRESS_DEFAULT + This is only required for the dedicated memory allocator, and specifies + the physical start address of the memory block reserved for UMP. +ARCH_UMP_MEMORY_SIZE_DEFAULT + This specified the size of the memory block reserved for UMP, or the + maximum limit for allocations from the OS. + +The result will be a ump.ko file, which can be loaded into the Linux kernel +by using the insmod command. The driver can also be built as a part of the +kernel itself. diff --git a/drivers/gpu/arm/umplock/Makefile b/drivers/gpu/arm/umplock/Makefile new file mode 100644 index 00000000000000..d1d5c4c7953d41 --- /dev/null +++ b/drivers/gpu/arm/umplock/Makefile @@ -0,0 +1,69 @@ +# +# Copyright (C) 2012 ARM Limited. All rights reserved. +# +# This program is free software and is provided to you under the terms of the GNU General Public License version 2 +# as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. +# +# A copy of the licence is included with the program, and can also be obtained from Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# + +# default to building for the host +ARCH ?= $(shell uname -m) + +# linux build system integration + +ifneq ($(KERNELRELEASE),) +# Inside the kernel build system + +EXTRA_CFLAGS += -I$(KBUILD_EXTMOD) + +SRC = umplock_driver.c + +MODULE:=umplock.ko + +obj-m := $(MODULE:.ko=.o) +$(MODULE:.ko=-y) := $(SRC:.c=.o) + +$(MODULE:.ko=-objs) := $(SRC:.c=.o) + +else +# Outside the kernel build system +# +# + +# Get any user defined KDIR- or maybe even a hardcoded KDIR +-include KDIR_CONFIGURATION + +# Define host system directory +KDIR-$(shell uname -m):=/lib/modules/$(shell uname -r)/build + +ifeq ($(ARCH), arm) + # when compiling for ARM we're cross compiling + export CROSS_COMPILE ?= arm-none-linux-gnueabi- + CONFIG ?= arm +else + # Compiling for the host + CONFIG ?= $(shell uname -m) +endif + +# default cpu to select +CPU ?= $(shell uname -m) + +# look up KDIR based om CPU selection +KDIR ?= $(KDIR-$(CPU)) + +ifeq ($(KDIR),) +$(error No KDIR found for platform $(CPU)) +endif + +all: + $(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) + +kernelrelease: + $(MAKE) -C $(KDIR) kernelrelease + +clean: + $(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) clean + +endif diff --git a/drivers/gpu/arm/umplock/umplock_driver.c b/drivers/gpu/arm/umplock/umplock_driver.c new file mode 100644 index 00000000000000..bd537b3906ad41 --- /dev/null +++ b/drivers/gpu/arm/umplock/umplock_driver.c @@ -0,0 +1,598 @@ +/* + * Copyright (C) 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "umplock_ioctl.h" +#include + +#define MAX_ITEMS 1024 +#define MAX_PIDS 128 + +typedef struct lock_cmd_priv { + uint32_t msg[128]; /*ioctl args*/ + u32 pid; /*process id*/ +} _lock_cmd_priv; + +typedef struct lock_ref { + int ref_count; + u32 pid; +} _lock_ref; + +typedef struct umplock_item { + u32 secure_id; + /*u32 references;*/ + _lock_access_usage usage; + _lock_ref references[MAX_PIDS]; + struct semaphore item_lock; +} umplock_item; + +typedef struct umplock_device_private { + struct mutex item_list_lock; + atomic_t sessions; + umplock_item items[MAX_ITEMS]; + u32 pids[MAX_PIDS]; +} umplock_device_private; + +struct umplock_device { + struct cdev cdev; + struct class *umplock_class; +}; + +static char umplock_dev_name[] = "umplock"; + +int umplock_major = 0; +module_param(umplock_major, int, S_IRUGO); /* r--r--r-- */ +MODULE_PARM_DESC(umplock_major, "Device major number"); + +static int umplock_driver_open( struct inode *inode, struct file *filp ); +static int umplock_driver_release( struct inode *inode, struct file *filp ); +static long umplock_driver_ioctl( struct file *f, unsigned int cmd, unsigned long arg ); + +static struct file_operations umplock_fops = { + .owner = THIS_MODULE, + .open = umplock_driver_open, + .release = umplock_driver_release, + .unlocked_ioctl = umplock_driver_ioctl, +}; + +static struct umplock_device umplock_device; +static umplock_device_private device; + +void umplock_init_locklist( void ) +{ + memset(&device.items, 0, sizeof(umplock_item)*MAX_ITEMS); + atomic_set(&device.sessions, 0); +} + +void umplock_deinit_locklist( void ) +{ + memset(&device.items, 0, sizeof(umplock_item)*MAX_ITEMS); +} + +int umplock_device_initialize( void ) +{ + int err; + dev_t dev = 0; + + if ( 0 == umplock_major ) { + err = alloc_chrdev_region(&dev, 0, 1, umplock_dev_name); + umplock_major = MAJOR(dev); + } else { + dev = MKDEV(umplock_major, 0); + err = register_chrdev_region(dev, 1, umplock_dev_name); + } + + if ( 0 == err ) { + memset(&umplock_device, 0, sizeof(umplock_device)); + cdev_init(&umplock_device.cdev, &umplock_fops); + umplock_device.cdev.owner = THIS_MODULE; + umplock_device.cdev.ops = &umplock_fops; + + err = cdev_add(&umplock_device.cdev, dev, 1); + if ( 0 == err ) { + umplock_device.umplock_class = class_create(THIS_MODULE, umplock_dev_name); + if ( IS_ERR(umplock_device.umplock_class ) ) { + err = PTR_ERR(umplock_device.umplock_class); + } else { + struct device *mdev; + mdev = device_create(umplock_device.umplock_class, NULL, dev, NULL, umplock_dev_name); + if ( !IS_ERR(mdev) ) { + return 0; /* all ok */ + } + + err = PTR_ERR(mdev); + class_destroy(umplock_device.umplock_class); + } + cdev_del(&umplock_device.cdev); + } + + unregister_chrdev_region(dev, 1); + } + + return 1; +} + +void umplock_device_terminate(void) +{ + dev_t dev = MKDEV(umplock_major, 0); + + device_destroy(umplock_device.umplock_class, dev); + class_destroy(umplock_device.umplock_class); + + cdev_del(&umplock_device.cdev); + unregister_chrdev_region(dev, 1); +} + +int umplock_constructor(void) +{ + mutex_init(&device.item_list_lock); + if ( !umplock_device_initialize() ) return 1; + umplock_init_locklist(); + + return 0; +} + +void umplock_destructor(void) +{ + umplock_deinit_locklist(); + umplock_device_terminate(); + mutex_destroy(&device.item_list_lock); +} + +int umplock_find_item( u32 secure_id ) +{ + int i; + for ( i=0; imsg; + + i = umplock_find_item(lock_item->secure_id); + + if ( i < 0) + return -1; + + for(j=0; jpid) { + *item_slot = i; + *ref_slot = j; + return 0; + } + } + return -1 ; +} + +static int umplock_find_client_valid(u32 pid) +{ + int i; + + if(pid == 0) + return -1; + + for(i=0; imsg; + + i_index = ref_index = -1; + +#if 0 + if ( lock_item->usage == 1 ) printk( KERN_DEBUG "UMPLOCK: C 0x%x GPU SURFACE\n", lock_item->secure_id ); + else if ( lock_item->usage == 2 ) printk( KERN_DEBUG "UMPLOCK: C 0x%x GPU TEXTURE\n", lock_item->secure_id ); + else printk( KERN_DEBUG "UMPLOCK: C 0x%x CPU\n", lock_item->secure_id ); +#endif + + ret = umplock_find_client_valid( lock_cmd->pid ); + if( ret < 0 ) { + /*lock request from an invalid client pid, do nothing*/ + return 0; + } + + ret = umplock_find_item_by_pid( lock_cmd, &i_index, &ref_index ); + if ( ret >= 0 ) { + if (device.items[i_index].references[ref_index].ref_count == 0) + device.items[i_index].references[ref_index].ref_count = 1; + } else if ( (i_index = umplock_find_item( lock_item->secure_id)) >= 0 ) { + for ( ref_index = 0; ref_index < MAX_PIDS; ref_index++) { + if (device.items[i_index].references[ref_index].pid == 0) break; + } + if ( ref_index < MAX_PIDS ) { + device.items[i_index].references[ref_index].pid = lock_cmd->pid; + device.items[i_index].references[ref_index].ref_count = 1; + } else { + printk( KERN_ERR "UMPLOCK: whoops, item ran out of available reference slot\n" ); + } + } else { + i_index = umplock_find_slot(); + + if ( i_index >= 0 ) { + device.items[i_index].secure_id = lock_item->secure_id; + device.items[i_index].usage = lock_item->usage; + device.items[i_index].references[0].pid = lock_cmd->pid; + device.items[i_index].references[0].ref_count = 1; + sema_init(&device.items[i_index].item_lock, 1); + } else { + printk( KERN_ERR "UMPLOCK: whoops, ran out of available slots\n" ); + } + } + + return 0; +} +/** IOCTLs **/ + +static int do_umplock_create(_lock_cmd_priv *lock_cmd) +{ + int ret = 0; + mutex_lock(&device.item_list_lock); + ret = do_umplock_create_locked(lock_cmd); + mutex_unlock(&device.item_list_lock); + return ret; +} + +static int do_umplock_process( _lock_cmd_priv *lock_cmd ) +{ + int ret, i_index, ref_index, ref_count; + _lock_item_s *lock_item = (_lock_item_s *)&lock_cmd->msg; + + mutex_lock(&device.item_list_lock); + + do_umplock_create_locked(lock_cmd); + + ret = umplock_find_client_valid( lock_cmd->pid ); + if( ret < 0 ) { + /*lock request from an invalid client pid, do nothing*/ + mutex_unlock(&device.item_list_lock); + return 0; + } + + ret = umplock_find_item_by_pid( lock_cmd, &i_index, &ref_index ); + ref_count = device.items[i_index].references[ref_index].ref_count; + if ( ret >= 0 ) { + if (ref_count == 1) { + /*add ref before down to wait for the umplock*/ + device.items[i_index].references[ref_index].ref_count++; + mutex_unlock(&device.item_list_lock); + if ( down_interruptible(&device.items[i_index].item_lock) ) { + /*wait up without hold the umplock. restore previous state and return*/ + mutex_lock(&device.item_list_lock); + device.items[i_index].references[ref_index].ref_count--; + mutex_unlock(&device.item_list_lock); + return -ERESTARTSYS; + } + mutex_lock(&device.item_list_lock); + } else { + /*already got the umplock, add ref*/ + device.items[i_index].references[ref_index].ref_count++; + } +#if 0 + if ( lock_item->usage == 1 ) printk( KERN_DEBUG "UMPLOCK: P 0x%x GPU SURFACE\n", lock_item->secure_id ); + else if ( lock_item->usage == 2 ) printk( KERN_DEBUG "UMPLOCK: P 0x%x GPU TEXTURE\n", lock_item->secure_id ); + else printk( KERN_DEBUG "UMPLOCK: P 0x%x CPU\n", lock_item->secure_id ); +#endif + } else { + /*fail to find a item*/ + printk(KERN_ERR "UMPLOCK: IOCTL_UMPLOCK_PROCESS called with invalid parameter\n"); + mutex_unlock(&device.item_list_lock); + return -EINVAL; + } + mutex_unlock(&device.item_list_lock); + return 0; +} + +static int do_umplock_release( _lock_cmd_priv *lock_cmd ) +{ + int i_index,ref_index, ref_count; + int ret; + _lock_item_s *lock_item = (_lock_item_s *)&lock_cmd->msg; + + mutex_lock(&device.item_list_lock); + ret = umplock_find_client_valid( lock_cmd->pid ); + if( ret < 0 ) { + /*lock request from an invalid client pid, do nothing*/ + mutex_unlock(&device.item_list_lock); + return 0; + } + + i_index = ref_index = -1; + + ret = umplock_find_item_by_pid( lock_cmd, &i_index, &ref_index ); + + if ( ret >= 0 ) { + device.items[i_index].references[ref_index].ref_count--; + ref_count = device.items[i_index].references[ref_index].ref_count; + +#if 0 + if ( lock_item->usage == 1 ) printk( KERN_DEBUG "UMPLOCK: R 0x%x GPU SURFACE\n", lock_item->secure_id ); + else if ( lock_item->usage == 2 ) printk( KERN_DEBUG "UMPLOCK: R 0x%x GPU TEXTURE\n", lock_item->secure_id ); + else printk( KERN_DEBUG "UMPLOCK: R 0x%x CPU\n", lock_item->secure_id ); +#endif + /*reached the last reference to the umplock*/ + if ( ref_count == 1 ) { + /*release the umplock*/ + up( &device.items[i_index].item_lock ); + + device.items[i_index].references[ref_index].ref_count = 0; + device.items[i_index].references[ref_index].pid = 0; + } + } else { + /*fail to find item*/ + printk(KERN_ERR "UMPLOCK: IOCTL_UMPLOCK_RELEASE called with invalid parameter\n"); + mutex_unlock(&device.item_list_lock); + return -EINVAL; + } + mutex_unlock(&device.item_list_lock); + return 0; +} + +static int do_umplock_zap( void ) +{ + int i; + + printk( KERN_DEBUG "UMPLOCK: ZAP ALL ENTRIES!\n" ); + + mutex_lock(&device.item_list_lock); + + for ( i=0; isecure_id=%d\t reference[%d].ref_count=%d.pid=%d\n", + i, + device.items[i].secure_id, + j, + device.items[i].references[j].ref_count, + device.items[i].references[j].pid); + } + } + } + mutex_unlock(&device.item_list_lock); + + return 0; +} + +int do_umplock_client_add (_lock_cmd_priv *lock_cmd ) +{ + int i; + mutex_lock(&device.item_list_lock); + for ( i= 0; ipid) { + return 0; + } + } + for ( i=0; ipid; + break; + } + } + mutex_unlock(&device.item_list_lock); + if( i==MAX_PIDS) { + printk(KERN_ERR "Oops, Run out of cient slots\n "); + } + return 0; +} + +int do_umplock_client_delete (_lock_cmd_priv *lock_cmd ) +{ + int p_index=-1, i_index=-1,ref_index=-1; + int ret; + _lock_item_s *lock_item; + lock_item = (_lock_item_s *)&lock_cmd->msg; + + mutex_lock(&device.item_list_lock); + p_index = umplock_find_client_valid( lock_cmd->pid ); + /*lock item pid is not valid.*/ + if ( p_index<0 ) { + mutex_unlock(&device.item_list_lock); + return 0; + } + + /*walk through umplock item list and release reference attached to this client*/ + for(i_index = 0; i_index< MAX_ITEMS; i_index++ ) { + lock_item->secure_id = device.items[i_index].secure_id; + /*find the item index and reference slot for the lock_item*/ + ret = umplock_find_item_by_pid(lock_cmd, &i_index, &ref_index); + + if(ret < 0) { + /*client has no reference on this umplock item, skip*/ + continue; + } + while(device.items[i_index].references[ref_index].ref_count) { + /*release references on this client*/ + mutex_unlock(&device.item_list_lock); + do_umplock_release(lock_cmd); + mutex_lock(&device.item_list_lock); + } + } + + /*remove the pid from umplock valid pid list*/ + device.pids[p_index] = 0; + mutex_unlock(&device.item_list_lock); + + return 0; +} + +static long umplock_driver_ioctl( struct file *f, unsigned int cmd, unsigned long arg ) +{ + int ret; + uint32_t size = _IOC_SIZE(cmd); + _lock_cmd_priv lock_cmd ; + + if (_IOC_TYPE(cmd) != LOCK_IOCTL_GROUP ) { + return -ENOTTY; + } + + if (_IOC_NR(cmd) >= LOCK_IOCTL_MAX_CMDS ) { + return -ENOTTY; + } + + switch ( cmd ) { + case LOCK_IOCTL_CREATE: + if (size != sizeof(_lock_item_s)) { + return -ENOTTY; + } + + if (copy_from_user(&lock_cmd.msg, (void __user *)arg, size)) { + return -EFAULT; + } + lock_cmd.pid = (u32)current->tgid; + ret = do_umplock_create(&lock_cmd); + if (ret) { + return ret; + } + return 0; + + case LOCK_IOCTL_PROCESS: + if (size != sizeof(_lock_item_s)) { + return -ENOTTY; + } + + if (copy_from_user(&lock_cmd.msg, (void __user *)arg, size)) { + return -EFAULT; + } + lock_cmd.pid = (u32)current->tgid; + return do_umplock_process(&lock_cmd); + + case LOCK_IOCTL_RELEASE: + if (size != sizeof(_lock_item_s)) { + return -ENOTTY; + } + + if (copy_from_user(&lock_cmd.msg, (void __user *)arg, size)) { + return -EFAULT; + } + lock_cmd.pid = (u32)current->tgid; + ret = do_umplock_release( &lock_cmd ); + if (ret) { + return ret; + } + return 0; + + case LOCK_IOCTL_ZAP: + do_umplock_zap(); + return 0; + + case LOCK_IOCTL_DUMP: + do_umplock_dump(); + return 0; + } + + return -ENOIOCTLCMD; +} + +static int umplock_driver_open( struct inode *inode, struct file *filp ) +{ + _lock_cmd_priv lock_cmd; + + atomic_inc(&device.sessions); + printk( KERN_DEBUG "UMPLOCK: OPEN SESSION (%i references)\n", atomic_read(&device.sessions) ); + + lock_cmd.pid = (u32)current->tgid; + do_umplock_client_add(&lock_cmd); + + return 0; +} + +static int umplock_driver_release( struct inode *inode, struct file *filp ) +{ + _lock_cmd_priv lock_cmd; + + lock_cmd.pid = (u32)current->tgid; + do_umplock_client_delete(&lock_cmd); + + atomic_dec(&device.sessions); + printk( KERN_DEBUG "UMPLOCK: CLOSE SESSION (%i references)\n", atomic_read(&device.sessions) ); + if ( atomic_read(&device.sessions) == 0 ) { + do_umplock_zap(); + } + + return 0; +} + +static int __init umplock_initialize_module( void ) +{ + printk( KERN_DEBUG "Inserting UMP lock device driver. Compiled: %s, time: %s\n", __DATE__, __TIME__ ); + + if ( !umplock_constructor() ) { + printk( KERN_ERR "UMP lock device driver init failed\n"); + return -ENOTTY; + } + + printk( KERN_DEBUG "UMP lock device driver loaded\n" ); + + return 0; +} + +static void __exit umplock_cleanup_module( void ) +{ + printk( KERN_DEBUG "unloading UMP lock module\n" ); + umplock_destructor(); + printk( KERN_DEBUG "UMP lock module unloaded\n" ); +} + +module_init(umplock_initialize_module); +module_exit(umplock_cleanup_module); + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("ARM Ltd."); +MODULE_DESCRIPTION("ARM UMP locker"); diff --git a/drivers/gpu/arm/umplock/umplock_ioctl.h b/drivers/gpu/arm/umplock/umplock_ioctl.h new file mode 100644 index 00000000000000..88b0a46fff4f16 --- /dev/null +++ b/drivers/gpu/arm/umplock/umplock_ioctl.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __UMPLOCK_IOCTL_H__ +#define __UMPLOCK_IOCTL_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#ifndef __user +#define __user +#endif + + +/** + * @file umplock_ioctl.h + * This file describes the interface needed to use the Linux device driver. + * The interface is used by the userpace Mali DDK. + */ + +typedef enum { + _LOCK_ACCESS_RENDERABLE = 1, + _LOCK_ACCESS_TEXTURE, + _LOCK_ACCESS_CPU_WRITE, + _LOCK_ACCESS_CPU_READ, +} _lock_access_usage; + +typedef struct _lock_item_s { + unsigned int secure_id; + _lock_access_usage usage; +} _lock_item_s; + + +#define LOCK_IOCTL_GROUP 0x91 + +#define _LOCK_IOCTL_CREATE_CMD 0 /* create kernel lock item */ +#define _LOCK_IOCTL_PROCESS_CMD 1 /* process kernel lock item */ +#define _LOCK_IOCTL_RELEASE_CMD 2 /* release kernel lock item */ +#define _LOCK_IOCTL_ZAP_CMD 3 /* clean up all kernel lock items */ +#define _LOCK_IOCTL_DUMP_CMD 4 /* dump all the items */ + +#define LOCK_IOCTL_MAX_CMDS 5 + +#define LOCK_IOCTL_CREATE _IOW( LOCK_IOCTL_GROUP, _LOCK_IOCTL_CREATE_CMD, _lock_item_s ) +#define LOCK_IOCTL_PROCESS _IOW( LOCK_IOCTL_GROUP, _LOCK_IOCTL_PROCESS_CMD, _lock_item_s ) +#define LOCK_IOCTL_RELEASE _IOW( LOCK_IOCTL_GROUP, _LOCK_IOCTL_RELEASE_CMD, _lock_item_s ) +#define LOCK_IOCTL_ZAP _IO ( LOCK_IOCTL_GROUP, _LOCK_IOCTL_ZAP_CMD ) +#define LOCK_IOCTL_DUMP _IO ( LOCK_IOCTL_GROUP, _LOCK_IOCTL_DUMP_CMD ) + +#ifdef __cplusplus +} +#endif + +#endif /* __UMPLOCK_IOCTL_H__ */ + diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index c3413b6adb17ca..a4d425f148a929 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -165,6 +165,12 @@ config DRM_SAVAGE Choose this option if you have a Savage3D/4/SuperSavage/Pro/Twister chipset. If M is selected the module will be called savage. +config DRM_MALI + tristate "Mali DRM supprt" + help + Choose this option if you have a Mali 200 or Mali 400 GPU. + If M is selected the module will be called mali_drm. + source "drivers/gpu/drm/exynos/Kconfig" source "drivers/gpu/drm/rockchip/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index e620807418ea75..b0121c0c32b8f0 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -65,6 +65,7 @@ obj-$(CONFIG_DRM_MSM) += msm/ obj-$(CONFIG_DRM_TEGRA) += tegra/ obj-$(CONFIG_DRM_STI) += sti/ obj-$(CONFIG_DRM_IMX) += imx/ +obj-$(CONFIG_DRM_MALI) += mali/ obj-y += i2c/ obj-y += panel/ obj-y += bridge/ diff --git a/drivers/gpu/drm/mali/Makefile b/drivers/gpu/drm/mali/Makefile new file mode 100644 index 00000000000000..c3f8d4731b7552 --- /dev/null +++ b/drivers/gpu/drm/mali/Makefile @@ -0,0 +1,19 @@ +# +# Copyright (C) 2010, 2012-2013 ARM Limited. All rights reserved. +# +# This program is free software and is provided to you under the terms of the GNU General Public License version 2 +# as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. +# +# A copy of the licence is included with the program, and can also be obtained from Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# +# Makefile for the drm device driver. This driver provides support for the +# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. + +ccflags-y = -Iinclude/drm +mali_drm-y := mali_drv.o mali_mm.o + +obj-$(CONFIG_DRM_MALI) += mali_drm.o + + diff --git a/drivers/gpu/drm/mali/mali_drv.c b/drivers/gpu/drm/mali/mali_drv.c new file mode 100644 index 00000000000000..8f2ed140c4d9d7 --- /dev/null +++ b/drivers/gpu/drm/mali/mali_drv.c @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2010, 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include "drmP.h" +#include "mali_drm.h" +#include "mali_drv.h" + +static struct platform_device *pdev; + +static int mali_platform_drm_probe(struct platform_device *pdev) +{ + return 0; +} + +static int mali_platform_drm_remove(struct platform_device *pdev) +{ + return 0; +} + +static int mali_platform_drm_suspend(struct platform_device *dev, pm_message_t state) +{ + return 0; +} + +static int mali_platform_drm_resume(struct platform_device *dev) +{ + return 0; +} + + +static char mali_drm_device_name[] = "mali_drm"; +static struct platform_driver platform_drm_driver = +{ + .probe = mali_platform_drm_probe, + .remove = mali_platform_drm_remove, + .suspend = mali_platform_drm_suspend, + .resume = mali_platform_drm_resume, + .driver = { + .name = mali_drm_device_name, + .owner = THIS_MODULE, + }, +}; + +#if 0 +static const struct drm_device_id dock_device_ids[] = +{ + {"MALIDRM", 0}, + {"", 0}, +}; +#endif + +static int mali_driver_load(struct drm_device *dev, unsigned long chipset) +{ + int ret; + unsigned long base, size; + drm_mali_private_t *dev_priv; + printk(KERN_ERR "DRM: mali_driver_load start\n"); + + dev_priv = drm_calloc(1, sizeof(drm_mali_private_t), DRM_MEM_DRIVER); + + if (dev_priv == NULL) + { + return -ENOMEM; + } + + dev->dev_private = (void *)dev_priv; + + if (NULL == dev->platformdev) + { + dev->platformdev = platform_device_register_simple(mali_drm_device_name, 0, NULL, 0); + pdev = dev->platformdev; + } + +#if 0 + base = drm_get_resource_start(dev, 1); + size = drm_get_resource_len(dev, 1); +#endif + ret = drm_sman_init(&dev_priv->sman, 2, 12, 8); + + if (ret) + { + drm_free(dev_priv, sizeof(dev_priv), DRM_MEM_DRIVER); + } + + //if ( ret ) kfree( dev_priv ); + + printk(KERN_ERR "DRM: mali_driver_load done\n"); + + return ret; +} + +static int mali_driver_unload(struct drm_device *dev) +{ + drm_mali_private_t *dev_priv = dev->dev_private; + printk(KERN_ERR "DRM: mali_driver_unload start\n"); + + drm_sman_takedown(&dev_priv->sman); + drm_free(dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER); + //kfree( dev_priv ); + printk(KERN_ERR "DRM: mali_driver_unload done\n"); + + return 0; +} + +static struct drm_driver driver = +{ + .driver_features = DRIVER_USE_PLATFORM_DEVICE, + .load = mali_driver_load, + .unload = mali_driver_unload, + .context_dtor = NULL, + .dma_quiescent = mali_idle, + .reclaim_buffers = NULL, + .reclaim_buffers_idlelocked = mali_reclaim_buffers_locked, + .lastclose = mali_lastclose, + .get_map_ofs = drm_core_get_map_ofs, + .get_reg_ofs = drm_core_get_reg_ofs, + .ioctls = mali_ioctls, + .fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39) + .ioctl = drm_ioctl, +#else + .unlocked_ioctl = drm_ioctl, +#endif + .mmap = drm_mmap, + .poll = drm_poll, + .fasync = drm_fasync, + }, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, +}; + +static int __init mali_init(void) +{ + driver.num_ioctls = mali_max_ioctl; + return drm_init(&driver); +} + +static void __exit mali_exit(void) +{ + platform_device_unregister(pdev); + drm_exit(&driver); +} + +module_init(mali_init); +module_exit(mali_exit); + +MODULE_INFO(vermagic, VERMAGIC_STRING); +MODULE_AUTHOR("ARM Ltd."); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL and additional rights"); diff --git a/drivers/gpu/drm/mali/mali_drv.h b/drivers/gpu/drm/mali/mali_drv.h new file mode 100644 index 00000000000000..9450e27ad9fe9d --- /dev/null +++ b/drivers/gpu/drm/mali/mali_drv.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2010, 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _MALI_DRV_H_ +#define _MALI_DRV_H_ + +#define DRIVER_AUTHOR "ARM" +#define DRIVER_NAME "mali_drm" +#define DRIVER_DESC "DRM module for Mali-200, Mali-400" +#define DRIVER_DATE "20100520" +#define DRIVER_MAJOR 0 +#define DRIVER_MINOR 1 +#define DRIVER_PATCHLEVEL 0 + +#include "drm_sman.h" + +typedef struct drm_mali_private +{ + drm_local_map_t *mmio; + unsigned int idle_fault; + struct drm_sman sman; + int vram_initialized; + unsigned long vram_offset; +} drm_mali_private_t; + +extern int mali_idle(struct drm_device *dev); +extern void mali_reclaim_buffers_locked(struct drm_device *dev, struct drm_file *file_priv); +extern void mali_lastclose(struct drm_device *dev); +extern struct drm_ioctl_desc mali_ioctls[]; +extern int mali_max_ioctl; + +#endif /* _MALI_DRV_H_ */ diff --git a/drivers/gpu/drm/mali/mali_mm.c b/drivers/gpu/drm/mali/mali_mm.c new file mode 100644 index 00000000000000..86b54b3d51b024 --- /dev/null +++ b/drivers/gpu/drm/mali/mali_mm.c @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2010, 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "drmP.h" +#include "mali_drm.h" +#include "mali_drv.h" + +#define VIDEO_TYPE 0 +#define MEM_TYPE 1 + +#define MALI_MM_ALIGN_SHIFT 4 +#define MALI_MM_ALIGN_MASK ( (1 << MALI_MM_ALIGN_SHIFT) - 1) + + +static void *mali_sman_mm_allocate(void *private, unsigned long size, unsigned alignment) +{ + printk(KERN_ERR "DRM: %s\n", __func__); + return NULL; +} + +static void mali_sman_mm_free(void *private, void *ref) +{ + printk(KERN_ERR "DRM: %s\n", __func__); +} + +static void mali_sman_mm_destroy(void *private) +{ + printk(KERN_ERR "DRM: %s\n", __func__); +} + +static unsigned long mali_sman_mm_offset(void *private, void *ref) +{ + printk(KERN_ERR "DRM: %s\n", __func__); + return ~((unsigned long)ref); +} + +static int mali_fb_init(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_mali_private_t *dev_priv = dev->dev_private; + drm_mali_fb_t *fb = data; + int ret; + printk(KERN_ERR "DRM: %s\n", __func__); + + mutex_lock(&dev->struct_mutex); + { + struct drm_sman_mm sman_mm; + sman_mm.private = (void *)0xFFFFFFFF; + sman_mm.allocate = mali_sman_mm_allocate; + sman_mm.free = mali_sman_mm_free; + sman_mm.destroy = mali_sman_mm_destroy; + sman_mm.offset = mali_sman_mm_offset; + ret = drm_sman_set_manager(&dev_priv->sman, VIDEO_TYPE, &sman_mm); + } + + if (ret) + { + DRM_ERROR("VRAM memory manager initialisation error\n"); + mutex_unlock(&dev->struct_mutex); + return ret; + } + + dev_priv->vram_initialized = 1; + dev_priv->vram_offset = fb->offset; + + mutex_unlock(&dev->struct_mutex); + DRM_DEBUG("offset = %u, size = %u\n", fb->offset, fb->size); + + return 0; +} + +static int mali_drm_alloc(struct drm_device *dev, struct drm_file *file_priv, void *data, int pool) +{ + drm_mali_private_t *dev_priv = dev->dev_private; + drm_mali_mem_t *mem = data; + int retval = 0; + struct drm_memblock_item *item; + printk(KERN_ERR "DRM: %s\n", __func__); + + mutex_lock(&dev->struct_mutex); + + if (0 == dev_priv->vram_initialized) + { + DRM_ERROR("Attempt to allocate from uninitialized memory manager.\n"); + mutex_unlock(&dev->struct_mutex); + return -EINVAL; + } + + mem->size = (mem->size + MALI_MM_ALIGN_MASK) >> MALI_MM_ALIGN_SHIFT; + item = drm_sman_alloc(&dev_priv->sman, pool, mem->size, 0, + (unsigned long)file_priv); + + mutex_unlock(&dev->struct_mutex); + + if (item) + { + mem->offset = dev_priv->vram_offset + (item->mm->offset(item->mm, item->mm_info) << MALI_MM_ALIGN_SHIFT); + mem->free = item->user_hash.key; + mem->size = mem->size << MALI_MM_ALIGN_SHIFT; + } + else + { + mem->offset = 0; + mem->size = 0; + mem->free = 0; + retval = -ENOMEM; + } + + DRM_DEBUG("alloc %d, size = %d, offset = %d\n", pool, mem->size, mem->offset); + + return retval; +} + +static int mali_drm_free(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_mali_private_t *dev_priv = dev->dev_private; + drm_mali_mem_t *mem = data; + int ret; + printk(KERN_ERR "DRM: %s\n", __func__); + + mutex_lock(&dev->struct_mutex); + ret = drm_sman_free_key(&dev_priv->sman, mem->free); + mutex_unlock(&dev->struct_mutex); + DRM_DEBUG("free = 0x%lx\n", mem->free); + + return ret; +} + +static int mali_fb_alloc(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + printk(KERN_ERR "DRM: %s\n", __func__); + return mali_drm_alloc(dev, file_priv, data, VIDEO_TYPE); +} + +static int mali_ioctl_mem_init(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_mali_private_t *dev_priv = dev->dev_private; + drm_mali_mem_t *mem = data; + int ret; + dev_priv = dev->dev_private; + printk(KERN_ERR "DRM: %s\n", __func__); + + mutex_lock(&dev->struct_mutex); + ret = drm_sman_set_range(&dev_priv->sman, MEM_TYPE, 0, mem->size >> MALI_MM_ALIGN_SHIFT); + + if (ret) + { + DRM_ERROR("MEM memory manager initialisation error\n"); + mutex_unlock(&dev->struct_mutex); + return ret; + } + + mutex_unlock(&dev->struct_mutex); + + return 0; +} + +static int mali_ioctl_mem_alloc(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + + printk(KERN_ERR "DRM: %s\n", __func__); + return mali_drm_alloc(dev, file_priv, data, MEM_TYPE); +} + +static drm_local_map_t *mem_reg_init(struct drm_device *dev) +{ + struct drm_map_list *entry; + drm_local_map_t *map; + printk(KERN_ERR "DRM: %s\n", __func__); + + list_for_each_entry(entry, &dev->maplist, head) + { + map = entry->map; + + if (!map) + { + continue; + } + + if (map->type == _DRM_REGISTERS) + { + return map; + } + } + return NULL; +} + +int mali_idle(struct drm_device *dev) +{ + drm_mali_private_t *dev_priv = dev->dev_private; + uint32_t idle_reg; + unsigned long end; + int i; + printk(KERN_ERR "DRM: %s\n", __func__); + + if (dev_priv->idle_fault) + { + return 0; + } + + return 0; +} + + +void mali_lastclose(struct drm_device *dev) +{ + drm_mali_private_t *dev_priv = dev->dev_private; + printk(KERN_ERR "DRM: %s\n", __func__); + + if (!dev_priv) + { + return; + } + + mutex_lock(&dev->struct_mutex); + drm_sman_cleanup(&dev_priv->sman); + dev_priv->vram_initialized = 0; + dev_priv->mmio = NULL; + mutex_unlock(&dev->struct_mutex); +} + +void mali_reclaim_buffers_locked(struct drm_device *dev, struct drm_file *file_priv) +{ + drm_mali_private_t *dev_priv = dev->dev_private; + printk(KERN_ERR "DRM: %s\n", __func__); + + mutex_lock(&dev->struct_mutex); + + if (drm_sman_owner_clean(&dev_priv->sman, (unsigned long)file_priv)) + { + mutex_unlock(&dev->struct_mutex); + return; + } + + if (dev->driver->dma_quiescent) + { + dev->driver->dma_quiescent(dev); + } + + drm_sman_owner_cleanup(&dev_priv->sman, (unsigned long)file_priv); + mutex_unlock(&dev->struct_mutex); + return; +} + +struct drm_ioctl_desc mali_ioctls[] = +{ + DRM_IOCTL_DEF(DRM_MALI_FB_ALLOC, mali_fb_alloc, DRM_AUTH), + DRM_IOCTL_DEF(DRM_MALI_FB_FREE, mali_drm_free, DRM_AUTH), + DRM_IOCTL_DEF(DRM_MALI_MEM_INIT, mali_ioctl_mem_init, DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_MALI_MEM_ALLOC, mali_ioctl_mem_alloc, DRM_AUTH), + DRM_IOCTL_DEF(DRM_MALI_MEM_FREE, mali_drm_free, DRM_AUTH), + DRM_IOCTL_DEF(DRM_MALI_FB_INIT, mali_fb_init, DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY), +}; + +int mali_max_ioctl = DRM_ARRAY_SIZE(mali_ioctls); diff --git a/include/uapi/drm/Kbuild b/include/uapi/drm/Kbuild index 2d9a25daab0542..7cdfe659e8b6c0 100644 --- a/include/uapi/drm/Kbuild +++ b/include/uapi/drm/Kbuild @@ -17,3 +17,4 @@ header-y += tegra_drm.h header-y += via_drm.h header-y += vmwgfx_drm.h header-y += msm_drm.h +header-y += mali_drm.h diff --git a/include/uapi/drm/mali_drm.h b/include/uapi/drm/mali_drm.h new file mode 100644 index 00000000000000..c4382232d4347f --- /dev/null +++ b/include/uapi/drm/mali_drm.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2010, 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_DRM_H__ +#define __MALI_DRM_H__ + +/* Mali specific ioctls */ +#define NOT_USED_0_3 +#define DRM_MALI_FB_ALLOC 0x04 +#define DRM_MALI_FB_FREE 0x05 +#define NOT_USED_6_12 +#define DRM_MALI_MEM_INIT 0x13 +#define DRM_MALI_MEM_ALLOC 0x14 +#define DRM_MALI_MEM_FREE 0x15 +#define DRM_MALI_FB_INIT 0x16 + +#define DRM_IOCTL_MALI_FB_ALLOC DRM_IOWR(DRM_COMMAND_BASE + DRM_MALI_FB_ALLOC, drm_mali_mem_t) +#define DRM_IOCTL_MALI_FB_FREE DRM_IOW( DRM_COMMAND_BASE + DRM_MALI_FB_FREE, drm_mali_mem_t) +#define DRM_IOCTL_MALI_MEM_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_MALI_MEM_INIT, drm_mali_mem_t) +#define DRM_IOCTL_MALI_MEM_ALLOC DRM_IOWR(DRM_COMMAND_BASE + DRM_MALI_MEM_ALLOC, drm_mali_mem_t) +#define DRM_IOCTL_MALI_MEM_FREE DRM_IOW( DRM_COMMAND_BASE + DRM_MALI_MEM_FREE, drm_mali_mem_t) +#define DRM_IOCTL_MALI_FB_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_MALI_FB_INIT, drm_mali_fb_t) + +typedef struct +{ + int context; + unsigned int offset; + unsigned int size; + unsigned long free; +} drm_mali_mem_t; + +typedef struct +{ + unsigned int offset, size; +} drm_mali_fb_t; + +#endif /* __MALI_DRM_H__ */ diff --git a/include/ump/ump_kernel_interface.h b/include/ump/ump_kernel_interface.h new file mode 100644 index 00000000000000..99cc697334b0ed --- /dev/null +++ b/include/ump/ump_kernel_interface.h @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2010, 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_interface.h + * + * This file contains the kernel space part of the UMP API. + */ + +#ifndef __UMP_KERNEL_INTERFACE_H__ +#define __UMP_KERNEL_INTERFACE_H__ + + +/** @defgroup ump_kernel_space_api UMP Kernel Space API + * @{ */ + + +#include "ump_kernel_platform.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * External representation of a UMP handle in kernel space. + */ +typedef void *ump_dd_handle; + +/** + * Typedef for a secure ID, a system wide identificator for UMP memory buffers. + */ +typedef unsigned int ump_secure_id; + + +/** + * Value to indicate an invalid UMP memory handle. + */ +#define UMP_DD_HANDLE_INVALID ((ump_dd_handle)0) + + +/** + * Value to indicate an invalid secure Id. + */ +#define UMP_INVALID_SECURE_ID ((ump_secure_id)-1) + + +/** + * UMP error codes for kernel space. + */ +typedef enum +{ + UMP_DD_SUCCESS, /**< indicates success */ + UMP_DD_INVALID, /**< indicates failure */ +} ump_dd_status_code; + + +/** + * Struct used to describe a physical block used by UMP memory + */ +typedef struct ump_dd_physical_block +{ + unsigned long addr; /**< The physical address of the block */ + unsigned long size; /**< The length of the block, typically page aligned */ +} ump_dd_physical_block; + + +/** + * Retrieves the secure ID for the specified UMP memory. + * + * This identificator is unique across the entire system, and uniquely identifies + * the specified UMP memory. This identificator can later be used through the + * @ref ump_dd_handle_create_from_secure_id "ump_dd_handle_create_from_secure_id" or + * @ref ump_handle_create_from_secure_id "ump_handle_create_from_secure_id" + * functions in order to access this UMP memory, for instance from another process. + * + * @note There is a user space equivalent function called @ref ump_secure_id_get "ump_secure_id_get" + * + * @see ump_dd_handle_create_from_secure_id + * @see ump_handle_create_from_secure_id + * @see ump_secure_id_get + * + * @param mem Handle to UMP memory. + * + * @return Returns the secure ID for the specified UMP memory. + */ +UMP_KERNEL_API_EXPORT ump_secure_id ump_dd_secure_id_get(ump_dd_handle mem); + + +/** + * Retrieves a handle to allocated UMP memory. + * + * The usage of UMP memory is reference counted, so this will increment the reference + * count by one for the specified UMP memory. + * Use @ref ump_dd_reference_release "ump_dd_reference_release" when there is no longer any + * use for the retrieved handle. + * + * @note There is a user space equivalent function called @ref ump_handle_create_from_secure_id "ump_handle_create_from_secure_id" + * + * @see ump_dd_reference_release + * @see ump_handle_create_from_secure_id + * + * @param secure_id The secure ID of the UMP memory to open, that can be retrieved using the @ref ump_secure_id_get "ump_secure_id_get " function. + * + * @return UMP_INVALID_MEMORY_HANDLE indicates failure, otherwise a valid handle is returned. + */ +UMP_KERNEL_API_EXPORT ump_dd_handle ump_dd_handle_create_from_secure_id(ump_secure_id secure_id); + + +/** + * Retrieves the number of physical blocks used by the specified UMP memory. + * + * This function retrieves the number of @ref ump_dd_physical_block "ump_dd_physical_block" structs needed + * to describe the physical memory layout of the given UMP memory. This can later be used when calling + * the functions @ref ump_dd_phys_blocks_get "ump_dd_phys_blocks_get" and + * @ref ump_dd_phys_block_get "ump_dd_phys_block_get". + * + * @see ump_dd_phys_blocks_get + * @see ump_dd_phys_block_get + * + * @param mem Handle to UMP memory. + * + * @return The number of ump_dd_physical_block structs required to describe the physical memory layout of the specified UMP memory. + */ +UMP_KERNEL_API_EXPORT unsigned long ump_dd_phys_block_count_get(ump_dd_handle mem); + + +/** + * Retrieves all physical memory block information for specified UMP memory. + * + * This function can be used by other device drivers in order to create MMU tables. + * + * @note This function will fail if the num_blocks parameter is either to large or to small. + * + * @see ump_dd_phys_block_get + * + * @param mem Handle to UMP memory. + * @param blocks An array of @ref ump_dd_physical_block "ump_dd_physical_block" structs that will receive the physical description. + * @param num_blocks The number of blocks to return in the blocks array. Use the function + * @ref ump_dd_phys_block_count_get "ump_dd_phys_block_count_get" first to determine the number of blocks required. + * + * @return UMP_DD_SUCCESS indicates success, UMP_DD_INVALID indicates failure. + */ +UMP_KERNEL_API_EXPORT ump_dd_status_code ump_dd_phys_blocks_get(ump_dd_handle mem, ump_dd_physical_block *blocks, unsigned long num_blocks); + + +/** + * Retrieves the physical memory block information for specified block for the specified UMP memory. + * + * This function can be used by other device drivers in order to create MMU tables. + * + * @note This function will return UMP_DD_INVALID if the specified index is out of range. + * + * @see ump_dd_phys_blocks_get + * + * @param mem Handle to UMP memory. + * @param index Which physical info block to retrieve. + * @param block Pointer to a @ref ump_dd_physical_block "ump_dd_physical_block" struct which will receive the requested information. + * + * @return UMP_DD_SUCCESS indicates success, UMP_DD_INVALID indicates failure. + */ +UMP_KERNEL_API_EXPORT ump_dd_status_code ump_dd_phys_block_get(ump_dd_handle mem, unsigned long index, ump_dd_physical_block *block); + + +/** + * Retrieves the actual size of the specified UMP memory. + * + * The size is reported in bytes, and is typically page aligned. + * + * @note There is a user space equivalent function called @ref ump_size_get "ump_size_get" + * + * @see ump_size_get + * + * @param mem Handle to UMP memory. + * + * @return Returns the allocated size of the specified UMP memory, in bytes. + */ +UMP_KERNEL_API_EXPORT unsigned long ump_dd_size_get(ump_dd_handle mem); + + +/** + * Adds an extra reference to the specified UMP memory. + * + * This function adds an extra reference to the specified UMP memory. This function should + * be used every time a UMP memory handle is duplicated, that is, assigned to another ump_dd_handle + * variable. The function @ref ump_dd_reference_release "ump_dd_reference_release" must then be used + * to release each copy of the UMP memory handle. + * + * @note You are not required to call @ref ump_dd_reference_add "ump_dd_reference_add" + * for UMP handles returned from + * @ref ump_dd_handle_create_from_secure_id "ump_dd_handle_create_from_secure_id", + * because these handles are already reference counted by this function. + * + * @note There is a user space equivalent function called @ref ump_reference_add "ump_reference_add" + * + * @see ump_reference_add + * + * @param mem Handle to UMP memory. + */ +UMP_KERNEL_API_EXPORT void ump_dd_reference_add(ump_dd_handle mem); + + +/** + * Releases a reference from the specified UMP memory. + * + * This function should be called once for every reference to the UMP memory handle. + * When the last reference is released, all resources associated with this UMP memory + * handle are freed. + * + * @note There is a user space equivalent function called @ref ump_reference_release "ump_reference_release" + * + * @see ump_reference_release + * + * @param mem Handle to UMP memory. + */ +UMP_KERNEL_API_EXPORT void ump_dd_reference_release(ump_dd_handle mem); + + +#ifdef __cplusplus +} +#endif + + +/** @} */ /* end group ump_kernel_space_api */ + + +#endif /* __UMP_KERNEL_INTERFACE_H__ */ diff --git a/include/ump/ump_kernel_interface_ref_drv.h b/include/ump/ump_kernel_interface_ref_drv.h new file mode 100644 index 00000000000000..8a65302c3e1fa0 --- /dev/null +++ b/include/ump/ump_kernel_interface_ref_drv.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2010, 2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_interface.h + */ + +#ifndef __UMP_KERNEL_INTERFACE_REF_DRV_H__ +#define __UMP_KERNEL_INTERFACE_REF_DRV_H__ + +#include "ump_kernel_interface.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Turn specified physical memory into UMP memory. */ +UMP_KERNEL_API_EXPORT ump_dd_handle ump_dd_handle_create_from_phys_blocks(ump_dd_physical_block *blocks, unsigned long num_blocks); + +#ifdef __cplusplus +} +#endif + +#endif /* __UMP_KERNEL_INTERFACE_REF_DRV_H__ */ diff --git a/include/ump/ump_kernel_platform.h b/include/ump/ump_kernel_platform.h new file mode 100644 index 00000000000000..06a54c33ec147d --- /dev/null +++ b/include/ump/ump_kernel_platform.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2010 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_platform.h + * + * This file should define UMP_KERNEL_API_EXPORT, + * which dictates how the UMP kernel API should be exported/imported. + * Modify this file, if needed, to match your platform setup. + */ + +#ifndef __UMP_KERNEL_PLATFORM_H__ +#define __UMP_KERNEL_PLATFORM_H__ + +/** @addtogroup ump_kernel_space_api + * @{ */ + +/** + * A define which controls how UMP kernel space API functions are imported and exported. + * This define should be set by the implementor of the UMP API. + */ + +#define UMP_KERNEL_API_EXPORT + +/** @} */ /* end group ump_kernel_space_api */ + + +#endif /* __UMP_KERNEL_PLATFORM_H__ */ From 6fd11c5aaac88ff4e7636b99ccdc82b59b56f7b7 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sat, 15 Feb 2014 23:18:07 +0100 Subject: [PATCH 719/788] mali: some build fixes Also remove compatibility code for older kernel version. TODO: - does not compile yet (need to include more of prahal's changes) --- drivers/gpu/arm/mali/Kbuild | 1 - .../gpu/arm/mali/linux/mali_kernel_linux.c | 17 ------- drivers/gpu/arm/mali/linux/mali_memory.c | 4 -- .../gpu/arm/mali/linux/mali_memory_os_alloc.c | 49 ++++++------------- .../gpu/arm/mali/linux/mali_memory_types.h | 2 +- drivers/gpu/arm/mali/linux/mali_memory_ump.c | 2 +- drivers/gpu/arm/mali/linux/mali_osk_pm.c | 10 ---- drivers/gpu/arm/mali/linux/mali_osk_wq.c | 6 +-- drivers/gpu/arm/mali/platform/arm/arm.c | 2 - drivers/gpu/arm/mali/readme.txt | 24 --------- drivers/gpu/arm/ump/Kbuild | 12 ++--- .../config.h | 14 +++++- drivers/gpu/arm/ump/common/ump_kernel_api.c | 2 +- .../gpu/arm/ump/common/ump_kernel_common.h | 2 +- .../ump/common/ump_kernel_memory_backend.h | 2 +- .../gpu/arm/ump/common/ump_kernel_ref_drv.c | 2 +- drivers/gpu/arm/ump/common/ump_kernel_types.h | 2 +- drivers/gpu/arm/ump/linux/ump_kernel_linux.c | 27 +--------- .../ump_kernel_memory_backend_dedicated.c | 4 -- .../ump/linux/ump_kernel_memory_backend_os.c | 4 -- .../gpu/arm/ump/linux/ump_osk_low_level_mem.c | 24 +-------- drivers/gpu/arm/ump/readme.txt | 28 ----------- drivers/gpu/arm/umplock/Kbuild | 13 +++++ drivers/gpu/arm/umplock/Kconfig | 5 ++ drivers/gpu/drm/mali/mali_drv.c | 28 +++++------ drivers/gpu/drm/mali/mali_drv.h | 2 +- drivers/gpu/drm/mali/mali_mm.c | 6 +-- drivers/media/Kconfig | 1 + 28 files changed, 77 insertions(+), 218 deletions(-) delete mode 100644 drivers/gpu/arm/mali/readme.txt rename drivers/gpu/arm/ump/{arch-pb-virtex5 => arch-exynos4412}/config.h (58%) delete mode 100644 drivers/gpu/arm/ump/readme.txt create mode 100644 drivers/gpu/arm/umplock/Kbuild create mode 100644 drivers/gpu/arm/umplock/Kconfig diff --git a/drivers/gpu/arm/mali/Kbuild b/drivers/gpu/arm/mali/Kbuild index 6b2b4305684164..c8da7a090b614d 100644 --- a/drivers/gpu/arm/mali/Kbuild +++ b/drivers/gpu/arm/mali/Kbuild @@ -146,7 +146,6 @@ ifeq ($(MALI_UPPER_HALF_SCHEDULING),1) ccflags-y += -DMALI_UPPER_HALF_SCHEDULING endif -ccflags-$(CONFIG_MALI400_UMP) += -I$(src)/../../ump/include/ump ccflags-$(CONFIG_MALI400_DEBUG) += -DDEBUG # Use our defines when compiling diff --git a/drivers/gpu/arm/mali/linux/mali_kernel_linux.c b/drivers/gpu/arm/mali/linux/mali_kernel_linux.c index 186b4dcddf45aa..56960ffbb57dee 100644 --- a/drivers/gpu/arm/mali/linux/mali_kernel_linux.c +++ b/drivers/gpu/arm/mali/linux/mali_kernel_linux.c @@ -150,17 +150,6 @@ extern int mali_platform_device_unregister(void); #endif /* Linux power management operations provided by the Mali device driver */ -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)) -struct pm_ext_ops mali_dev_ext_pm_ops = { - .base = - { - .suspend = mali_driver_suspend_scheduler, - .resume = mali_driver_resume_scheduler, - .freeze = mali_driver_suspend_scheduler, - .thaw = mali_driver_resume_scheduler, - }, -}; -#else static const struct dev_pm_ops mali_dev_pm_ops = { #ifdef CONFIG_PM_RUNTIME .runtime_suspend = mali_driver_runtime_suspend, @@ -173,23 +162,17 @@ static const struct dev_pm_ops mali_dev_pm_ops = { .thaw = mali_driver_resume_scheduler, .poweroff = mali_driver_suspend_scheduler, }; -#endif /* The Mali device driver struct */ static struct platform_driver mali_platform_driver = { .probe = mali_probe, .remove = mali_remove, -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)) - .pm = &mali_dev_ext_pm_ops, -#endif .driver = { .name = MALI_GPU_NAME_UTGARD, .owner = THIS_MODULE, .bus = &platform_bus_type, -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) .pm = &mali_dev_pm_ops, -#endif }, }; diff --git a/drivers/gpu/arm/mali/linux/mali_memory.c b/drivers/gpu/arm/mali/linux/mali_memory.c index f0c465806c7ae8..f9441de07a9840 100644 --- a/drivers/gpu/arm/mali/linux/mali_memory.c +++ b/drivers/gpu/arm/mali/linux/mali_memory.c @@ -159,12 +159,8 @@ int mali_mmap(struct file *filp, struct vm_area_struct *vma) vma->vm_flags |= VM_IO; vma->vm_flags |= VM_DONTCOPY; vma->vm_flags |= VM_PFNMAP; -#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) - vma->vm_flags |= VM_RESERVED; -#else vma->vm_flags |= VM_DONTDUMP; vma->vm_flags |= VM_DONTEXPAND; -#endif vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); vma->vm_ops = &mali_kernel_vm_ops; /* Operations used on any memory system */ diff --git a/drivers/gpu/arm/mali/linux/mali_memory_os_alloc.c b/drivers/gpu/arm/mali/linux/mali_memory_os_alloc.c index a9b20354f46689..99126ee191e28a 100644 --- a/drivers/gpu/arm/mali/linux/mali_memory_os_alloc.c +++ b/drivers/gpu/arm/mali/linux/mali_memory_os_alloc.c @@ -26,15 +26,8 @@ #define MALI_OS_MEMORY_KERNEL_BUFFER_SIZE_IN_PAGES (MALI_OS_MEMORY_KERNEL_BUFFER_SIZE_IN_MB * 256) #define MALI_OS_MEMORY_POOL_TRIM_JIFFIES (10 * CONFIG_HZ) /* Default to 10s */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) -static int mali_mem_os_shrink(int nr_to_scan, gfp_t gfp_mask); -#else -static int mali_mem_os_shrink(struct shrinker *shrinker, int nr_to_scan, gfp_t gfp_mask); -#endif -#else -static int mali_mem_os_shrink(struct shrinker *shrinker, struct shrink_control *sc); -#endif +static unsigned long mali_mem_os_shrink_count(struct shrinker *shrinker, struct shrink_control *sc); +static unsigned long mali_mem_os_shrink_scan(struct shrinker *shrinker, struct shrink_control *sc); static void mali_mem_os_trim_pool(struct work_struct *work); static struct mali_mem_os_allocator { @@ -56,15 +49,10 @@ static struct mali_mem_os_allocator { .allocated_pages = ATOMIC_INIT(0), .allocation_limit = 0, - .shrinker.shrink = mali_mem_os_shrink, + .shrinker.count_objects = mali_mem_os_shrink_count, + .shrinker.scan_objects = mali_mem_os_shrink_scan, .shrinker.seeks = DEFAULT_SEEKS, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) .timed_shrinker = __DELAYED_WORK_INITIALIZER(mali_mem_os_allocator.timed_shrinker, mali_mem_os_trim_pool, TIMER_DEFERRABLE), -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38) - .timed_shrinker = __DEFERRED_WORK_INITIALIZER(mali_mem_os_allocator.timed_shrinker, mali_mem_os_trim_pool), -#else - .timed_shrinker = __DELAYED_WORK_INITIALIZER(mali_mem_os_allocator.timed_shrinker, mali_mem_os_trim_pool), -#endif }; static void mali_mem_os_free(mali_mem_allocation *descriptor) @@ -402,32 +390,22 @@ static void mali_mem_os_trim_page_table_page_pool(void) mali_mem_os_page_table_pool_free(nr_to_free); } -#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) -static int mali_mem_os_shrink(int nr_to_scan, gfp_t gfp_mask) -#else -static int mali_mem_os_shrink(struct shrinker *shrinker, int nr_to_scan, gfp_t gfp_mask) -#endif -#else -static int mali_mem_os_shrink(struct shrinker *shrinker, struct shrink_control *sc) -#endif +static unsigned long mali_mem_os_shrink_count(struct shrinker *shrinker, struct shrink_control *sc) +{ + return mali_mem_os_allocator.pool_count + mali_mem_page_table_page_pool.count; +} + +static unsigned long mali_mem_os_shrink_scan(struct shrinker *shrinker, struct shrink_control *sc) { struct page *page, *tmp; unsigned long flags; struct list_head *le, pages; -#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) - int nr = nr_to_scan; -#else int nr = sc->nr_to_scan; -#endif - - if (0 == nr) { - return mali_mem_os_allocator.pool_count + mali_mem_page_table_page_pool.count; - } + unsigned long freed = 0; if (0 == mali_mem_os_allocator.pool_count) { /* No pages availble */ - return 0; + return SHRINK_STOP; } if (0 == spin_trylock_irqsave(&mali_mem_os_allocator.pool_lock, flags)) { @@ -440,6 +418,7 @@ static int mali_mem_os_shrink(struct shrinker *shrinker, struct shrink_control * mali_mem_os_allocator.pool_count -= nr; list_for_each(le, &mali_mem_os_allocator.pool_pages) { --nr; + freed++; if (0 == nr) break; } list_cut_position(&pages, &mali_mem_os_allocator.pool_pages, le); @@ -458,7 +437,7 @@ static int mali_mem_os_shrink(struct shrinker *shrinker, struct shrink_control * cancel_delayed_work(&mali_mem_os_allocator.timed_shrinker); } - return mali_mem_os_allocator.pool_count + mali_mem_page_table_page_pool.count; + return freed; } static void mali_mem_os_trim_pool(struct work_struct *data) diff --git a/drivers/gpu/arm/mali/linux/mali_memory_types.h b/drivers/gpu/arm/mali/linux/mali_memory_types.h index 10672785a2339b..8b01f3b584a8ca 100644 --- a/drivers/gpu/arm/mali/linux/mali_memory_types.h +++ b/drivers/gpu/arm/mali/linux/mali_memory_types.h @@ -12,7 +12,7 @@ #define __MALI_MEMORY_TYPES_H__ #if defined(CONFIG_MALI400_UMP) -#include "ump_kernel_interface.h" +#include #endif typedef u32 mali_address_t; diff --git a/drivers/gpu/arm/mali/linux/mali_memory_ump.c b/drivers/gpu/arm/mali/linux/mali_memory_ump.c index 1115c1d1add0bc..c5dca465ee3539 100644 --- a/drivers/gpu/arm/mali/linux/mali_memory_ump.c +++ b/drivers/gpu/arm/mali/linux/mali_memory_ump.c @@ -16,7 +16,7 @@ #include "mali_memory.h" -#include "ump_kernel_interface.h" +#include static int mali_ump_map(struct mali_session_data *session, mali_mem_allocation *descriptor) { diff --git a/drivers/gpu/arm/mali/linux/mali_osk_pm.c b/drivers/gpu/arm/mali/linux/mali_osk_pm.c index f8769b189a09cf..b7f224dd8bc7e7 100644 --- a/drivers/gpu/arm/mali/linux/mali_osk_pm.c +++ b/drivers/gpu/arm/mali/linux/mali_osk_pm.c @@ -43,9 +43,7 @@ _mali_osk_errcode_t _mali_osk_pm_dev_ref_add(void) int err; MALI_DEBUG_ASSERT_POINTER(mali_platform_device); err = pm_runtime_get_sync(&(mali_platform_device->dev)); -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) pm_runtime_mark_last_busy(&(mali_platform_device->dev)); -#endif if (0 > err) { MALI_PRINT_ERROR(("Mali OSK PM: pm_runtime_get_sync() returned error code %d\n", err)); return _MALI_OSK_ERR_FAULT; @@ -62,12 +60,8 @@ void _mali_osk_pm_dev_ref_dec(void) #ifdef CONFIG_PM_RUNTIME MALI_DEBUG_ASSERT_POINTER(mali_platform_device); _mali_osk_atomic_dec(&mali_pm_ref_count); -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) pm_runtime_mark_last_busy(&(mali_platform_device->dev)); pm_runtime_put_autosuspend(&(mali_platform_device->dev)); -#else - pm_runtime_put(&(mali_platform_device->dev)); -#endif MALI_DEBUG_PRINT(4, ("Mali OSK PM: Power ref released (%u)\n", _mali_osk_atomic_read(&mali_pm_ref_count))); #endif } @@ -92,11 +86,7 @@ void _mali_osk_pm_dev_ref_dec_no_power_on(void) { #ifdef CONFIG_PM_RUNTIME MALI_DEBUG_ASSERT_POINTER(mali_platform_device); -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) pm_runtime_put_autosuspend(&(mali_platform_device->dev)); -#else - pm_runtime_put(&(mali_platform_device->dev)); -#endif MALI_DEBUG_PRINT(4, ("Mali OSK PM: No-power ref released (%u)\n", _mali_osk_atomic_read(&mali_pm_ref_count))); #endif } diff --git a/drivers/gpu/arm/mali/linux/mali_osk_wq.c b/drivers/gpu/arm/mali/linux/mali_osk_wq.c index d07977ae42a5ef..0ae6733c952ab5 100644 --- a/drivers/gpu/arm/mali/linux/mali_osk_wq.c +++ b/drivers/gpu/arm/mali/linux/mali_osk_wq.c @@ -49,13 +49,9 @@ _mali_osk_errcode_t _mali_osk_wq_init(void) MALI_DEBUG_ASSERT(NULL == mali_wq_normal); MALI_DEBUG_ASSERT(NULL == mali_wq_high); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) mali_wq_normal = alloc_workqueue("mali", WQ_UNBOUND, 0); mali_wq_high = alloc_workqueue("mali_high_pri", WQ_HIGHPRI, 0); -#else - mali_wq_normal = create_workqueue("mali"); - mali_wq_high = create_workqueue("mali_high_pri"); -#endif + if (NULL == mali_wq_normal || NULL == mali_wq_high) { MALI_PRINT_ERROR(("Unable to create Mali workqueues\n")); diff --git a/drivers/gpu/arm/mali/platform/arm/arm.c b/drivers/gpu/arm/mali/platform/arm/arm.c index 7ab66ce3aa5305..a3da4c0e10fa47 100644 --- a/drivers/gpu/arm/mali/platform/arm/arm.c +++ b/drivers/gpu/arm/mali/platform/arm/arm.c @@ -139,10 +139,8 @@ int mali_platform_device_register(void) err = platform_device_register(&mali_gpu_device); if (0 == err) { #ifdef CONFIG_PM_RUNTIME -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) pm_runtime_set_autosuspend_delay(&(mali_gpu_device.dev), 1000); pm_runtime_use_autosuspend(&(mali_gpu_device.dev)); -#endif pm_runtime_enable(&(mali_gpu_device.dev)); #endif MALI_DEBUG_ASSERT(0 < num_pp_cores); diff --git a/drivers/gpu/arm/mali/readme.txt b/drivers/gpu/arm/mali/readme.txt deleted file mode 100644 index 26095066bb42b4..00000000000000 --- a/drivers/gpu/arm/mali/readme.txt +++ /dev/null @@ -1,24 +0,0 @@ -Building the Mali Device Driver for Linux ------------------------------------------ - -Build the Mali Device Driver for Linux by running the following make command: - -KDIR= USING_UMP= BUILD= make - -where - kdir_path: Path to your Linux Kernel directory - ump_option: 1 = Enable UMP support(*) - 0 = disable UMP support - build_option: debug = debug build of driver - release = release build of driver - -(*) For newer Linux Kernels, the Module.symvers file for the UMP device driver - must be available. The UMP_SYMVERS_FILE variable in the Makefile should - point to this file. This file is generated when the UMP driver is built. - -The result will be a mali.ko file, which can be loaded into the Linux kernel -by using the insmod command. - -The kernel needs to be provided with a platform_device struct for the Mali GPU -device. See the mali_utgard.h header file for how to set up the Mali GPU -resources. diff --git a/drivers/gpu/arm/ump/Kbuild b/drivers/gpu/arm/ump/Kbuild index 4f62b785317787..54d91b149c4a0d 100644 --- a/drivers/gpu/arm/ump/Kbuild +++ b/drivers/gpu/arm/ump/Kbuild @@ -10,7 +10,7 @@ # Set default configuration to use, if Makefile didn't provide one. # Change this to use a different config.h -CONFIG ?= os_memory_64m +CONFIG ?= exynos4412 # Validate selected config ifneq ($(shell [ -d $(src)/arch-$(CONFIG) ] && [ -f $(src)/arch-$(CONFIG)/config.h ] && echo "OK"), OK) @@ -56,7 +56,7 @@ endif ccflags-y += -DSVN_REV=$(SVN_REV) ccflags-y += -DSVN_REV_STRING=\"$(DRIVER_REV)\" -ccflags-y += -I$(src) -I$(src)/common -I$(src)/linux -I$(src)/../mali/common -I$(src)/../mali/linux -I$(src)/../../ump/include/ump +ccflags-y += -I$(src) -I$(src)/common -I$(src)/linux -I$(src)/../mali/common -I$(src)/../mali/linux ccflags-y += -DMALI_STATE_TRACKING=0 ccflags-y += -DMALI_ENABLE_CPU_CYCLES=0 ccflags-$(CONFIG_UMP_DEBUG) += -DDEBUG @@ -83,12 +83,6 @@ ump-y = common/ump_kernel_common.o \ linux/ump_ukk_ref_wrappers.o \ linux/ump_osk_atomics.o \ linux/ump_osk_low_level_mem.o \ - linux/ump_osk_misc.o \ - $(UDD_FILE_PREFIX)linux/mali_osk_atomics.o \ - $(UDD_FILE_PREFIX)linux/mali_osk_locks.o \ - $(UDD_FILE_PREFIX)linux/mali_osk_memory.o \ - $(UDD_FILE_PREFIX)linux/mali_osk_math.o \ - $(UDD_FILE_PREFIX)linux/mali_osk_misc.o + linux/ump_osk_misc.o obj-$(CONFIG_UMP) := ump.o - diff --git a/drivers/gpu/arm/ump/arch-pb-virtex5/config.h b/drivers/gpu/arm/ump/arch-exynos4412/config.h similarity index 58% rename from drivers/gpu/arm/ump/arch-pb-virtex5/config.h rename to drivers/gpu/arm/ump/arch-exynos4412/config.h index 850e28d5e99f35..c2608822f3ca35 100644 --- a/drivers/gpu/arm/ump/arch-pb-virtex5/config.h +++ b/drivers/gpu/arm/ump/arch-exynos4412/config.h @@ -11,7 +11,19 @@ #ifndef __ARCH_CONFIG_H__ #define __ARCH_CONFIG_H__ -#define ARCH_UMP_BACKEND_DEFAULT 0 +/* +ARCH_UMP_BACKEND_DEFAULT + 0 specifies the dedicated memory allocator. + 1 specifies the OS memory allocator. +ARCH_UMP_MEMORY_ADDRESS_DEFAULT + This is only required for the dedicated memory allocator, and specifies + the physical start address of the memory block reserved for UMP. +ARCH_UMP_MEMORY_SIZE_DEFAULT + This specified the size of the memory block reserved for UMP, or the + maximum limit for allocations from the OS. +*/ + +#define ARCH_UMP_BACKEND_DEFAULT 1 #define ARCH_UMP_MEMORY_ADDRESS_DEFAULT 0xE1000000 #define ARCH_UMP_MEMORY_SIZE_DEFAULT 16UL * 1024UL * 1024UL diff --git a/drivers/gpu/arm/ump/common/ump_kernel_api.c b/drivers/gpu/arm/ump/common/ump_kernel_api.c index 000a91287164be..58df0a2d31e4a9 100644 --- a/drivers/gpu/arm/ump/common/ump_kernel_api.c +++ b/drivers/gpu/arm/ump/common/ump_kernel_api.c @@ -12,7 +12,7 @@ #include "mali_osk_list.h" #include "ump_osk.h" #include "ump_uk_types.h" -#include "ump_kernel_interface.h" +#include #include "ump_kernel_common.h" diff --git a/drivers/gpu/arm/ump/common/ump_kernel_common.h b/drivers/gpu/arm/ump/common/ump_kernel_common.h index fa4639ff0c74ea..49cea7ee7477e6 100644 --- a/drivers/gpu/arm/ump/common/ump_kernel_common.h +++ b/drivers/gpu/arm/ump/common/ump_kernel_common.h @@ -12,7 +12,7 @@ #define __UMP_KERNEL_COMMON_H__ #include "ump_kernel_types.h" -#include "ump_kernel_interface.h" +#include #include "ump_kernel_descriptor_mapping.h" #include "ump_kernel_memory_backend.h" diff --git a/drivers/gpu/arm/ump/common/ump_kernel_memory_backend.h b/drivers/gpu/arm/ump/common/ump_kernel_memory_backend.h index 705c705d89c713..887a2c7d89d74b 100644 --- a/drivers/gpu/arm/ump/common/ump_kernel_memory_backend.h +++ b/drivers/gpu/arm/ump/common/ump_kernel_memory_backend.h @@ -15,7 +15,7 @@ #ifndef __UMP_KERNEL_MEMORY_BACKEND_H__ #define __UMP_KERNEL_MEMORY_BACKEND_H__ -#include "ump_kernel_interface.h" +#include #include "ump_kernel_types.h" diff --git a/drivers/gpu/arm/ump/common/ump_kernel_ref_drv.c b/drivers/gpu/arm/ump/common/ump_kernel_ref_drv.c index 99eb17217b9863..14320fc86bd7cb 100644 --- a/drivers/gpu/arm/ump/common/ump_kernel_ref_drv.c +++ b/drivers/gpu/arm/ump/common/ump_kernel_ref_drv.c @@ -13,7 +13,7 @@ #include "ump_osk.h" #include "ump_uk_types.h" -#include "ump_kernel_interface_ref_drv.h" +#include #include "ump_kernel_common.h" #include "ump_kernel_descriptor_mapping.h" diff --git a/drivers/gpu/arm/ump/common/ump_kernel_types.h b/drivers/gpu/arm/ump/common/ump_kernel_types.h index 08704ab4053feb..081fab3caf3204 100644 --- a/drivers/gpu/arm/ump/common/ump_kernel_types.h +++ b/drivers/gpu/arm/ump/common/ump_kernel_types.h @@ -11,7 +11,7 @@ #ifndef __UMP_KERNEL_TYPES_H__ #define __UMP_KERNEL_TYPES_H__ -#include "ump_kernel_interface.h" +#include #include "mali_osk.h" diff --git a/drivers/gpu/arm/ump/linux/ump_kernel_linux.c b/drivers/gpu/arm/ump/linux/ump_kernel_linux.c index b22585da7c85ca..d75fba55995bd8 100644 --- a/drivers/gpu/arm/ump/linux/ump_kernel_linux.c +++ b/drivers/gpu/arm/ump/linux/ump_kernel_linux.c @@ -21,8 +21,8 @@ #include "arch/config.h" /* Configuration for current platform. The symlinc for arch is set by Makefile */ #include "ump_ioctl.h" #include "ump_kernel_common.h" -#include "ump_kernel_interface.h" -#include "ump_kernel_interface_ref_drv.h" +#include +#include #include "ump_kernel_descriptor_mapping.h" #include "ump_kernel_memory_backend.h" #include "ump_kernel_memory_backend_os.h" @@ -356,29 +356,6 @@ static int ump_file_ioctl(struct inode *inode, struct file *filp, unsigned int c return err; } -int map_errcode( _mali_osk_errcode_t err ) -{ - switch(err) { - case _MALI_OSK_ERR_OK : - return 0; - case _MALI_OSK_ERR_FAULT: - return -EFAULT; - case _MALI_OSK_ERR_INVALID_FUNC: - return -ENOTTY; - case _MALI_OSK_ERR_INVALID_ARGS: - return -EINVAL; - case _MALI_OSK_ERR_NOMEM: - return -ENOMEM; - case _MALI_OSK_ERR_TIMEOUT: - return -ETIMEDOUT; - case _MALI_OSK_ERR_RESTARTSYSCALL: - return -ERESTARTSYS; - case _MALI_OSK_ERR_ITEM_NOT_FOUND: - return -ENOENT; - default: - return -EFAULT; - } -} /* * Handle from OS to map specified virtual memory to specified UMP memory. diff --git a/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_dedicated.c b/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_dedicated.c index ca276c844de830..ff443293409bf4 100644 --- a/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_dedicated.c +++ b/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_dedicated.c @@ -11,11 +11,7 @@ /* needed to detect kernel version specific code */ #include -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) #include -#else /* pre 2.6.26 the file was in the arch specific location */ -#include -#endif #include #include diff --git a/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_os.c b/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_os.c index 61817c74ac8e13..d3fa08025c2adc 100644 --- a/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_os.c +++ b/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_os.c @@ -11,11 +11,7 @@ /* needed to detect kernel version specific code */ #include -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) #include -#else /* pre 2.6.26 the file was in the arch specific location */ -#include -#endif #include #include diff --git a/drivers/gpu/arm/ump/linux/ump_osk_low_level_mem.c b/drivers/gpu/arm/ump/linux/ump_osk_low_level_mem.c index 0736c93331aa01..f19858382e6381 100644 --- a/drivers/gpu/arm/ump/linux/ump_osk_low_level_mem.c +++ b/drivers/gpu/arm/ump/linux/ump_osk_low_level_mem.c @@ -37,44 +37,27 @@ typedef struct ump_vma_usage_tracker { static void ump_vma_open(struct vm_area_struct * vma); static void ump_vma_close(struct vm_area_struct * vma); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) static int ump_cpu_page_fault_handler(struct vm_area_struct *vma, struct vm_fault *vmf); -#else -static unsigned long ump_cpu_page_fault_handler(struct vm_area_struct * vma, unsigned long address); -#endif static struct vm_operations_struct ump_vm_ops = { .open = ump_vma_open, .close = ump_vma_close, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) .fault = ump_cpu_page_fault_handler -#else - .nopfn = ump_cpu_page_fault_handler -#endif }; /* * Page fault for VMA region * This should never happen since we always map in the entire virtual memory range. */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) static int ump_cpu_page_fault_handler(struct vm_area_struct *vma, struct vm_fault *vmf) -#else -static unsigned long ump_cpu_page_fault_handler(struct vm_area_struct * vma, unsigned long address) -#endif { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) void __user * address; address = vmf->virtual_address; -#endif + MSG_ERR(("Page-fault in UMP memory region caused by the CPU\n")); MSG_ERR(("VMA: 0x%08lx, virtual address: 0x%08lx\n", (unsigned long)vma, address)); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) return VM_FAULT_SIGBUS; -#else - return NOPFN_SIGBUS; -#endif } static void ump_vma_open(struct vm_area_struct * vma) @@ -143,14 +126,9 @@ _mali_osk_errcode_t _ump_osk_mem_mapregion_init( ump_memory_allocation * descrip vma->vm_private_data = vma_usage_tracker; vma->vm_flags |= VM_IO; -#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) - vma->vm_flags |= VM_RESERVED; -#else vma->vm_flags |= VM_DONTDUMP; vma->vm_flags |= VM_DONTEXPAND; vma->vm_flags |= VM_PFNMAP; -#endif - if (0==descriptor->is_cached) { vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); diff --git a/drivers/gpu/arm/ump/readme.txt b/drivers/gpu/arm/ump/readme.txt deleted file mode 100644 index c238cf0f2b1f0b..00000000000000 --- a/drivers/gpu/arm/ump/readme.txt +++ /dev/null @@ -1,28 +0,0 @@ -Building the UMP Device Driver for Linux ----------------------------------------- - -Build the UMP Device Driver for Linux by running the following make command: - -KDIR= CONFIG= BUILD= make - -where - kdir_path: Path to your Linux Kernel directory - your_config: Name of the sub-folder to find the required config.h file - ("arch-" will be prepended) - build_option: debug or release. Debug is default. - -The config.h contains following configuration parameters: - -ARCH_UMP_BACKEND_DEFAULT - 0 specifies the dedicated memory allocator. - 1 specifies the OS memory allocator. -ARCH_UMP_MEMORY_ADDRESS_DEFAULT - This is only required for the dedicated memory allocator, and specifies - the physical start address of the memory block reserved for UMP. -ARCH_UMP_MEMORY_SIZE_DEFAULT - This specified the size of the memory block reserved for UMP, or the - maximum limit for allocations from the OS. - -The result will be a ump.ko file, which can be loaded into the Linux kernel -by using the insmod command. The driver can also be built as a part of the -kernel itself. diff --git a/drivers/gpu/arm/umplock/Kbuild b/drivers/gpu/arm/umplock/Kbuild new file mode 100644 index 00000000000000..654f1a2e48c4ac --- /dev/null +++ b/drivers/gpu/arm/umplock/Kbuild @@ -0,0 +1,13 @@ +# +# Copyright (C) 2010-2012 ARM Limited. All rights reserved. +# +# This program is free software and is provided to you under the terms of the GNU General Public License version 2 +# as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. +# +# A copy of the licence is included with the program, and can also be obtained from Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# + +umplock-y = umplock_driver.o + +obj-$(CONFIG_UMPLOCK) := umplock.o diff --git a/drivers/gpu/arm/umplock/Kconfig b/drivers/gpu/arm/umplock/Kconfig new file mode 100644 index 00000000000000..2c9b6781839c79 --- /dev/null +++ b/drivers/gpu/arm/umplock/Kconfig @@ -0,0 +1,5 @@ +config UMPLOCK + tristate "UMP locking support" + depends on UMP + help + UMP locking driver support. diff --git a/drivers/gpu/drm/mali/mali_drv.c b/drivers/gpu/drm/mali/mali_drv.c index 8f2ed140c4d9d7..627a1f5cdf2d84 100644 --- a/drivers/gpu/drm/mali/mali_drv.c +++ b/drivers/gpu/drm/mali/mali_drv.c @@ -10,8 +10,8 @@ #include #include -#include "drmP.h" -#include "mali_drm.h" +#include +#include #include "mali_drv.h" static struct platform_device *pdev; @@ -111,6 +111,16 @@ static int mali_driver_unload(struct drm_device *dev) return 0; } +static const struct file_operations mali_driver_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .mmap = drm_mmap, + .poll = drm_poll, + .unlocked_ioctl = drm_ioctl, + .release = drm_release, + .fasync = drm_fasync, +}; + static struct drm_driver driver = { .driver_features = DRIVER_USE_PLATFORM_DEVICE, @@ -124,19 +134,7 @@ static struct drm_driver driver = .get_map_ofs = drm_core_get_map_ofs, .get_reg_ofs = drm_core_get_reg_ofs, .ioctls = mali_ioctls, - .fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39) - .ioctl = drm_ioctl, -#else - .unlocked_ioctl = drm_ioctl, -#endif - .mmap = drm_mmap, - .poll = drm_poll, - .fasync = drm_fasync, - }, + .fops = mali_driver_fops, .name = DRIVER_NAME, .desc = DRIVER_DESC, .date = DRIVER_DATE, diff --git a/drivers/gpu/drm/mali/mali_drv.h b/drivers/gpu/drm/mali/mali_drv.h index 9450e27ad9fe9d..8187e94f4e4c91 100644 --- a/drivers/gpu/drm/mali/mali_drv.h +++ b/drivers/gpu/drm/mali/mali_drv.h @@ -33,7 +33,7 @@ typedef struct drm_mali_private extern int mali_idle(struct drm_device *dev); extern void mali_reclaim_buffers_locked(struct drm_device *dev, struct drm_file *file_priv); extern void mali_lastclose(struct drm_device *dev); -extern struct drm_ioctl_desc mali_ioctls[]; +extern const struct drm_ioctl_desc mali_ioctls[]; extern int mali_max_ioctl; #endif /* _MALI_DRV_H_ */ diff --git a/drivers/gpu/drm/mali/mali_mm.c b/drivers/gpu/drm/mali/mali_mm.c index 86b54b3d51b024..f195c9f229a40d 100644 --- a/drivers/gpu/drm/mali/mali_mm.c +++ b/drivers/gpu/drm/mali/mali_mm.c @@ -8,8 +8,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "drmP.h" -#include "mali_drm.h" +#include +#include #include "mali_drv.h" #define VIDEO_TYPE 0 @@ -249,7 +249,7 @@ void mali_reclaim_buffers_locked(struct drm_device *dev, struct drm_file *file_p return; } -struct drm_ioctl_desc mali_ioctls[] = +const struct drm_ioctl_desc mali_ioctls[] = { DRM_IOCTL_DEF(DRM_MALI_FB_ALLOC, mali_fb_alloc, DRM_AUTH), DRM_IOCTL_DEF(DRM_MALI_FB_FREE, mali_drm_free, DRM_AUTH), diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index e0653a1150c923..c0da0da18137fb 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -212,5 +212,6 @@ config MEDIA_ATTACH source "drivers/media/i2c/Kconfig" source "drivers/media/tuners/Kconfig" source "drivers/media/dvb-frontends/Kconfig" +source "drivers/gpu/arm/Kconfig" endif # MEDIA_SUPPORT From 67d239722a9989bad37fdd572e5922a87214028e Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sat, 17 Jan 2015 02:27:09 +0100 Subject: [PATCH 720/788] cpufreq: exynos: Enable Prime frequences/voltages for Exynos4412 WARNING: Don't use this on a regular Exynos4412, since the higher voltages might damage the board/SoC. --- drivers/cpufreq/exynos4x12-cpufreq.c | 39 ++++++++++++++++------------ 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/drivers/cpufreq/exynos4x12-cpufreq.c b/drivers/cpufreq/exynos4x12-cpufreq.c index 9e78a850e29f4d..380552539a63dc 100644 --- a/drivers/cpufreq/exynos4x12-cpufreq.c +++ b/drivers/cpufreq/exynos4x12-cpufreq.c @@ -28,25 +28,29 @@ static struct clk *mout_apll; static struct exynos_dvfs_info *cpufreq; static unsigned int exynos4x12_volt_table[] = { - 1350000, 1287500, 1250000, 1187500, 1137500, 1087500, 1037500, - 1000000, 987500, 975000, 950000, 925000, 900000, 900000 + 1400000, 1350000, 1350000, 1300000, 1225000, 1175000, + 1125000, 1075000, 1037500, 1012500, 1000000, 987500, + 975000, 925000, 925000, 925000, 900000 }; static struct cpufreq_frequency_table exynos4x12_freq_table[] = { - {CPUFREQ_BOOST_FREQ, L0, 1500 * 1000}, - {0, L1, 1400 * 1000}, - {0, L2, 1300 * 1000}, - {0, L3, 1200 * 1000}, - {0, L4, 1100 * 1000}, - {0, L5, 1000 * 1000}, - {0, L6, 900 * 1000}, - {0, L7, 800 * 1000}, - {0, L8, 700 * 1000}, - {0, L9, 600 * 1000}, - {0, L10, 500 * 1000}, - {0, L11, 400 * 1000}, - {0, L12, 300 * 1000}, - {0, L13, 200 * 1000}, + {CPUFREQ_BOOST_FREQ, L0, 1800 * 1000}, + {0, L1, 1704 * 1000}, + {0, L2, 1600 * 1000}, + {0, L3, 1500 * 1000}, + {0, L4, 1400 * 1000}, + {0, L5, 1300 * 1000}, + {0, L6, 1200 * 1000}, + {0, L7, 1100 * 1000}, + {0, L8, 1000 * 1000}, + {0, L9, 900 * 1000}, + {0, L10, 800 * 1000}, + {0, L11, 700 * 1000}, + {0, L12, 600 * 1000}, + {0, L13, 500 * 1000}, + {0, L14, 400 * 1000}, + {0, L15, 300 * 1000}, + {0, L16, 200 * 1000}, {0, 0, CPUFREQ_TABLE_END}, }; @@ -84,6 +88,9 @@ static struct apll_freq apll_freq_4412[] = { * clock divider for COPY, HPM, CORES * PLL M, P, S */ + APLL_FREQ(1800, 0, 3, 7, 0, 6, 1, 2, 0, 7, 0, 7, 300, 4, 0), + APLL_FREQ(1704, 0, 3, 7, 0, 6, 1, 2, 0, 7, 0, 7, 213, 3, 0), + APLL_FREQ(1600, 0, 3, 7, 0, 6, 1, 2, 0, 6, 0, 7, 200, 3, 0), APLL_FREQ(1500, 0, 3, 7, 0, 6, 1, 2, 0, 6, 0, 7, 250, 4, 0), APLL_FREQ(1400, 0, 3, 7, 0, 6, 1, 2, 0, 6, 0, 6, 175, 3, 0), APLL_FREQ(1300, 0, 3, 7, 0, 5, 1, 2, 0, 5, 0, 6, 325, 6, 0), From db68d142ec66aefff25b88048d1bfa799570743e Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Mon, 19 Jan 2015 20:53:46 +0100 Subject: [PATCH 721/788] ARM: dts: exynos4412-odroid: Allow CPU voltage up to 1.4V WARNING: This is unsafe for the ODROID-X, which is powered only by a regular Exynos4412 SoC. --- arch/arm/boot/dts/exynos4412-odroid-common.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index 85ee0b799b07e8..33e037bd42a700 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -362,7 +362,7 @@ buck2_reg: BUCK2 { regulator-name = "vdd_arm"; regulator-min-microvolt = <900000>; - regulator-max-microvolt = <1350000>; + regulator-max-microvolt = <1400000>; regulator-always-on; regulator-boot-on; }; From 363bbe748dab0898c3fed0a544990418ec644545 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Wed, 21 Jan 2015 23:29:08 +0100 Subject: [PATCH 722/788] ARM: dts: exynos4412-odroid: unify voltage regulator style Use 'ldoX_reg: ldo@X' together with the 'regulator-compatible' property. --- .../boot/dts/exynos4412-odroid-common.dtsi | 72 ++++++++++++------- 1 file changed, 48 insertions(+), 24 deletions(-) diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index 33e037bd42a700..6e3ba0205388dc 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -216,28 +216,32 @@ #clock-cells = <1>; voltage-regulators { - ldo1_reg: LDO1 { + ldo1_reg: ldo@1 { + regulator-compatible = "LDO1"; regulator-name = "VDD_ALIVE_1.0V"; regulator-min-microvolt = <1000000>; regulator-max-microvolt = <1000000>; regulator-always-on; }; - ldo2_reg: LDO2 { + ldo2_reg: ldo@2 { + regulator-compatible = "LDO2"; regulator-name = "VDDQ_M1_2_1.8V"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; regulator-always-on; }; - ldo3_reg: LDO3 { + ldo3_reg: ldo@3 { + regulator-compatible = "LDO3"; regulator-name = "VDDQ_EXT_1.8V"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; regulator-always-on; }; - ldo4_reg: LDO4 { + ldo4_reg: ldo@4 { + regulator-compatible = "LDO4"; regulator-name = "VDDQ_MMC2_2.8V"; regulator-min-microvolt = <2800000>; regulator-max-microvolt = <2800000>; @@ -245,7 +249,8 @@ regulator-boot-on; }; - ldo5_reg: LDO5 { + ldo5_reg: ldo@5 { + regulator-compatible = "LDO5"; regulator-name = "VDDQ_MMC1_3_1.8V"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; @@ -253,14 +258,16 @@ regulator-boot-on; }; - ldo6_reg: LDO6 { + ldo6_reg: ldo@6 { + regulator-compatible = "LDO6"; regulator-name = "VDD10_MPLL_1.0V"; regulator-min-microvolt = <1000000>; regulator-max-microvolt = <1000000>; regulator-always-on; }; - ldo7_reg: LDO7 { + ldo7_reg: ldo@7 { + regulator-compatible = "LDO7"; regulator-name = "VDD10_XPLL_1.0V"; regulator-min-microvolt = <1000000>; regulator-max-microvolt = <1000000>; @@ -281,14 +288,16 @@ regulator-max-microvolt = <1800000>; }; - ldo11_reg: LDO11 { + ldo11_reg: ldo@11 { + regulator-compatible = "LDO11"; regulator-name = "VDD18_ABB1_1.8V"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; regulator-always-on; }; - ldo12_reg: LDO12 { + ldo12_reg: ldo@12 { + regulator-compatible = "LDO12"; regulator-name = "VDD33_USB_3.3V"; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; @@ -296,7 +305,8 @@ regulator-boot-on; }; - ldo13_reg: LDO13 { + ldo13_reg: ldo@13 { + regulator-compatible = "LDO13"; regulator-name = "VDDQ_C2C_W_1.8V"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; @@ -304,7 +314,8 @@ regulator-boot-on; }; - ldo14_reg: LDO14 { + ldo14_reg: ldo@14 { + regulator-compatible = "LDO14"; regulator-name = "VDD18_ABB0_2_1.8V"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; @@ -312,7 +323,8 @@ regulator-boot-on; }; - ldo15_reg: LDO15 { + ldo15_reg: ldo@15 { + regulator-compatible = "LDO15"; regulator-name = "VDD10_HSIC_1.0V"; regulator-min-microvolt = <1000000>; regulator-max-microvolt = <1000000>; @@ -320,7 +332,8 @@ regulator-boot-on; }; - ldo16_reg: LDO16 { + ldo16_reg: ldo@16 { + regulator-compatible = "LDO16"; regulator-name = "VDD18_HSIC_1.8V"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; @@ -328,14 +341,16 @@ regulator-boot-on; }; - ldo20_reg: LDO20 { + ldo20_reg: ldo@20 { + regulator-compatible = "LDO20"; regulator-name = "LDO20_1.8V"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; regulator-boot-on; }; - ldo21_reg: LDO21 { + ldo21_reg: ldo@21 { + regulator-compatible = "LDO21"; regulator-name = "LDO21_3.3V"; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; @@ -343,7 +358,8 @@ regulator-boot-on; }; - ldo25_reg: LDO25 { + ldo25_reg: ldo@25 { + regulator-compatible = "LDO25"; regulator-name = "VDDQ_LCD_1.8V"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; @@ -351,7 +367,8 @@ regulator-boot-on; }; - buck1_reg: BUCK1 { + buck1_reg: buck@1 { + regulator-compatible = "BUCK1"; regulator-name = "vdd_mif"; regulator-min-microvolt = <1000000>; regulator-max-microvolt = <1000000>; @@ -359,7 +376,8 @@ regulator-boot-on; }; - buck2_reg: BUCK2 { + buck2_reg: buck@2 { + regulator-compatible = "BUCK2"; regulator-name = "vdd_arm"; regulator-min-microvolt = <900000>; regulator-max-microvolt = <1400000>; @@ -367,7 +385,8 @@ regulator-boot-on; }; - buck3_reg: BUCK3 { + buck3_reg: buck@3 { + regulator-compatible = "BUCK3"; regulator-name = "vdd_int"; regulator-min-microvolt = <1000000>; regulator-max-microvolt = <1000000>; @@ -375,14 +394,16 @@ regulator-boot-on; }; - buck4_reg: BUCK4 { + buck4_reg: buck@4 { + regulator-compatible = "BUCK4"; regulator-name = "vdd_g3d"; regulator-min-microvolt = <900000>; regulator-max-microvolt = <1100000>; regulator-microvolt-offset = <50000>; }; - buck5_reg: BUCK5 { + buck5_reg: buck@5 { + regulator-compatible = "BUCK5"; regulator-name = "VDDQ_CKEM1_2_1.2V"; regulator-min-microvolt = <1200000>; regulator-max-microvolt = <1200000>; @@ -390,7 +411,8 @@ regulator-boot-on; }; - buck6_reg: BUCK6 { + buck6_reg: buck@6 { + regulator-compatible = "BUCK6"; regulator-name = "BUCK6_1.35V"; regulator-min-microvolt = <1350000>; regulator-max-microvolt = <1350000>; @@ -398,14 +420,16 @@ regulator-boot-on; }; - buck7_reg: BUCK7 { + buck7_reg: buck@7 { + regulator-compatible = "BUCK7"; regulator-name = "BUCK7_2.0V"; regulator-min-microvolt = <2000000>; regulator-max-microvolt = <2000000>; regulator-always-on; }; - buck8_reg: BUCK8 { + buck8_reg: buck@8 { + regulator-compatible = "BUCK8"; regulator-name = "BUCK8_2.8V"; regulator-min-microvolt = <2800000>; regulator-max-microvolt = <2800000>; From b90e7ddc695f563b59f3bb8455fa07488adf4639 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Thu, 28 Aug 2014 18:07:26 +0200 Subject: [PATCH 723/788] ARM: dts: exynos4412-odroid: adjust regular naming style The buck regulator names are all lowercase, so adjust the LDO regulator names accordingly. Also add comments to some regulators documenting their function (what subsystems does this regulator supply) on the board. The MMC subsystem (eMMC and SDHC) is especially confusing. Information for documenting was taken from the Hardkernel vendor kernel and by private communication with the Hardkernel developers. Also make the names less redundant. For example 'VDD18_ABB1_1.8V' has the voltage value in two places. The first time after the 'VDD' ('18') and then again as postfix ('1.8V'). --- .../boot/dts/exynos4412-odroid-common.dtsi | 54 +++++++++++-------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index 6e3ba0205388dc..c2784d6b5fbc1f 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -218,7 +218,7 @@ voltage-regulators { ldo1_reg: ldo@1 { regulator-compatible = "LDO1"; - regulator-name = "VDD_ALIVE_1.0V"; + regulator-name = "vdd_alive_1.0V"; regulator-min-microvolt = <1000000>; regulator-max-microvolt = <1000000>; regulator-always-on; @@ -226,32 +226,37 @@ ldo2_reg: ldo@2 { regulator-compatible = "LDO2"; - regulator-name = "VDDQ_M1_2_1.8V"; + regulator-name = "vddq_m1_m2_1.8V"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; regulator-always-on; }; + /* This regulator also supplies: * + * vddq_sbus, vddq_sys02, vddq_aud and more */ ldo3_reg: ldo@3 { regulator-compatible = "LDO3"; - regulator-name = "VDDQ_EXT_1.8V"; + regulator-name = "vddq_ext_1.8V"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; regulator-always-on; }; + /* Supply for IO of SDHC. */ ldo4_reg: ldo@4 { regulator-compatible = "LDO4"; - regulator-name = "VDDQ_MMC2_2.8V"; + regulator-name = "vddq_mmc2_2.8V"; regulator-min-microvolt = <2800000>; regulator-max-microvolt = <2800000>; regulator-always-on; regulator-boot-on; }; + /* The LDO5 regulator provides reference voltage for the whole MMC * + * subsystem (both SDHC and eMMC), so leave it always enabled. */ ldo5_reg: ldo@5 { regulator-compatible = "LDO5"; - regulator-name = "VDDQ_MMC1_3_1.8V"; + regulator-name = "vddq_mmc1_mmc3_1.8V"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; regulator-always-on; @@ -260,7 +265,7 @@ ldo6_reg: ldo@6 { regulator-compatible = "LDO6"; - regulator-name = "VDD10_MPLL_1.0V"; + regulator-name = "vdd_mpll_1.0V"; regulator-min-microvolt = <1000000>; regulator-max-microvolt = <1000000>; regulator-always-on; @@ -268,7 +273,7 @@ ldo7_reg: ldo@7 { regulator-compatible = "LDO7"; - regulator-name = "VDD10_XPLL_1.0V"; + regulator-name = "vdd_epll_1.0V"; regulator-min-microvolt = <1000000>; regulator-max-microvolt = <1000000>; regulator-always-on; @@ -276,21 +281,22 @@ ldo8_reg: ldo@8 { regulator-compatible = "LDO8"; - regulator-name = "VDD10_HDMI_1.0V"; + regulator-name = "vdd_hdmi_1.0V"; regulator-min-microvolt = <1000000>; regulator-max-microvolt = <1000000>; }; + /* This regulator also supplies the TMU block. */ ldo10_reg: ldo@10 { regulator-compatible = "LDO10"; - regulator-name = "VDDQ_MIPIHSI_1.8V"; + regulator-name = "vddq_mipihsi_1.8V"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; }; ldo11_reg: ldo@11 { regulator-compatible = "LDO11"; - regulator-name = "VDD18_ABB1_1.8V"; + regulator-name = "vdd_abb1_1.8V"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; regulator-always-on; @@ -298,7 +304,7 @@ ldo12_reg: ldo@12 { regulator-compatible = "LDO12"; - regulator-name = "VDD33_USB_3.3V"; + regulator-name = "vdd_usb_otg_3.3V"; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; regulator-always-on; @@ -307,7 +313,7 @@ ldo13_reg: ldo@13 { regulator-compatible = "LDO13"; - regulator-name = "VDDQ_C2C_W_1.8V"; + regulator-name = "vddq_c2c_w_1.8V"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; regulator-always-on; @@ -316,7 +322,7 @@ ldo14_reg: ldo@14 { regulator-compatible = "LDO14"; - regulator-name = "VDD18_ABB0_2_1.8V"; + regulator-name = "vdd_abb0_abb2_1.8V"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; regulator-always-on; @@ -325,7 +331,7 @@ ldo15_reg: ldo@15 { regulator-compatible = "LDO15"; - regulator-name = "VDD10_HSIC_1.0V"; + regulator-name = "vdd_otg_hsic_1.0V"; regulator-min-microvolt = <1000000>; regulator-max-microvolt = <1000000>; regulator-always-on; @@ -334,24 +340,26 @@ ldo16_reg: ldo@16 { regulator-compatible = "LDO16"; - regulator-name = "VDD18_HSIC_1.8V"; + regulator-name = "vdd_hsic_1.8V"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; regulator-always-on; regulator-boot-on; }; + /* Supply for IO of eMMC. */ ldo20_reg: ldo@20 { regulator-compatible = "LDO20"; - regulator-name = "LDO20_1.8V"; + regulator-name = "vddq_emmc_1.8V"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; regulator-boot-on; }; + /* Supply for core of SDHC. */ ldo21_reg: ldo@21 { regulator-compatible = "LDO21"; - regulator-name = "LDO21_3.3V"; + regulator-name = "tflash_3.3V"; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; regulator-always-on; @@ -360,7 +368,7 @@ ldo25_reg: ldo@25 { regulator-compatible = "LDO25"; - regulator-name = "VDDQ_LCD_1.8V"; + regulator-name = "vddq_lcd_1.8V"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; regulator-always-on; @@ -402,27 +410,31 @@ regulator-microvolt-offset = <50000>; }; + /* This regulator also supplies: * + * vddq_ckem2, vddq_e{1,2} and vddca_e{1,2} */ buck5_reg: buck@5 { regulator-compatible = "BUCK5"; - regulator-name = "VDDQ_CKEM1_2_1.2V"; + regulator-name = "vddq_ckem1_1.2V"; regulator-min-microvolt = <1200000>; regulator-max-microvolt = <1200000>; regulator-always-on; regulator-boot-on; }; + /* Input to LDO: 1, 6, 7, 8 and 15 */ buck6_reg: buck@6 { regulator-compatible = "BUCK6"; - regulator-name = "BUCK6_1.35V"; + regulator-name = "input_ldo_1.35V"; regulator-min-microvolt = <1350000>; regulator-max-microvolt = <1350000>; regulator-always-on; regulator-boot-on; }; + /* Input to LDO: 3, 5, 9, 11 and 17~20 */ buck7_reg: buck@7 { regulator-compatible = "BUCK7"; - regulator-name = "BUCK7_2.0V"; + regulator-name = "input_ldo_2.0V"; regulator-min-microvolt = <2000000>; regulator-max-microvolt = <2000000>; regulator-always-on; From 3561e92a6fbe4a1fe9239a3373be1e95a2aeb998 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Thu, 28 Aug 2014 18:28:06 +0200 Subject: [PATCH 724/788] ARM: dts: exynos4412-odroid: adjust regulator voltages Some voltages / voltage ranges differ from the ones that are used in the Hardkernel vendor kernel (HVK). Since the HVK is heavily tested with respect to system stability, use these values instead. While the HVK uses a voltage range of 9~15V for BUCK2 (vdd_arm), we currently keep the range of 8~14V, especially since exynos-cpufreq only uses operating parameters in this range. TODO: I'm not sure how well these values work for the ODROID-X, where the SoC is not an Exynos4412 Prime. --- .../boot/dts/exynos4412-odroid-common.dtsi | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index c2784d6b5fbc1f..171b7f74dd7c5c 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -352,16 +352,16 @@ regulator-compatible = "LDO20"; regulator-name = "vddq_emmc_1.8V"; regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; + regulator-max-microvolt = <3000000>; regulator-boot-on; }; /* Supply for core of SDHC. */ ldo21_reg: ldo@21 { regulator-compatible = "LDO21"; - regulator-name = "tflash_3.3V"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; + regulator-name = "tflash_2.8V"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; regulator-always-on; regulator-boot-on; }; @@ -378,8 +378,8 @@ buck1_reg: buck@1 { regulator-compatible = "BUCK1"; regulator-name = "vdd_mif"; - regulator-min-microvolt = <1000000>; - regulator-max-microvolt = <1000000>; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; regulator-always-on; regulator-boot-on; }; @@ -396,8 +396,8 @@ buck3_reg: buck@3 { regulator-compatible = "BUCK3"; regulator-name = "vdd_int"; - regulator-min-microvolt = <1000000>; - regulator-max-microvolt = <1000000>; + regulator-min-microvolt = <1125000>; + regulator-max-microvolt = <1125000>; regulator-always-on; regulator-boot-on; }; @@ -405,8 +405,8 @@ buck4_reg: buck@4 { regulator-compatible = "BUCK4"; regulator-name = "vdd_g3d"; - regulator-min-microvolt = <900000>; - regulator-max-microvolt = <1100000>; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <1200000>; regulator-microvolt-offset = <50000>; }; From 1fe7f35a3527a897950197b5f33e7f831c6e1fa7 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Thu, 28 Aug 2014 18:42:41 +0200 Subject: [PATCH 725/788] ARM: dts: exynos4412-odroid: separate buck8 regulator Move the buck8 regulator to the respective board dts files, since it is used differently depending on the board. On the Odroid-X2 buck8 is used as supply for the eMMC, with the vendor kernel setting it up with a voltage of 2.85V. On the Odroid-U2/U3 it is the supply for the LAN9730 block on the board, which provides the ethernet functionality. The vendor kernel sets a voltage for 3.3V in the case. --- arch/arm/boot/dts/exynos4412-odroid-common.dtsi | 7 ------- arch/arm/boot/dts/exynos4412-odroidu3.dts | 12 ++++++++++++ arch/arm/boot/dts/exynos4412-odroidx.dts | 12 ++++++++++++ 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index 171b7f74dd7c5c..03518d5890a8d9 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -439,13 +439,6 @@ regulator-max-microvolt = <2000000>; regulator-always-on; }; - - buck8_reg: buck@8 { - regulator-compatible = "BUCK8"; - regulator-name = "BUCK8_2.8V"; - regulator-min-microvolt = <2800000>; - regulator-max-microvolt = <2800000>; - }; }; }; }; diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts b/arch/arm/boot/dts/exynos4412-odroidu3.dts index c9a34be70aed79..fa21981253af0f 100644 --- a/arch/arm/boot/dts/exynos4412-odroidu3.dts +++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts @@ -110,6 +110,18 @@ refclk-frequency = <24000000>; }; +&max77686 { + voltage-regulators { + /* On the Odroid-U2/U3 this buck regulator supplies the LAN9730 block. */ + buck8_reg: buck@8 { + regulator-compatible = "BUCK8"; + regulator-name = "input_lan9730_3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + }; +}; + &ehci { port@1 { status = "okay"; diff --git a/arch/arm/boot/dts/exynos4412-odroidx.dts b/arch/arm/boot/dts/exynos4412-odroidx.dts index cb1cfe7239c443..67c7a01066d8df 100644 --- a/arch/arm/boot/dts/exynos4412-odroidx.dts +++ b/arch/arm/boot/dts/exynos4412-odroidx.dts @@ -71,6 +71,18 @@ }; }; +&max77686 { + voltage-regulators { + /* On the Odroid-X2 this buck regulator supplies the core of the eMMC. */ + buck8_reg: buck@8 { + regulator-compatible = "BUCK8"; + regulator-name = "vddf_emmc_2.85V"; + regulator-min-microvolt = <2850000>; + regulator-max-microvolt = <2850000>; + }; + }; +}; + &ehci { port@1 { status = "okay"; From 840562eff49bba56e5ef5dc2c17e3f1a5daec282 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Thu, 28 Aug 2014 18:50:26 +0200 Subject: [PATCH 726/788] ARM: dts: exynos4412-odroid: add LDO22 regulator On the Odroid-U2/U3 this regulator is used for the eMMC. It is unused on the Odroid-X2, but adding it makes it possible to turn it off then, so saving a bit of power (in case the bootloader doesn't already switch it off). --- arch/arm/boot/dts/exynos4412-odroidu3.dts | 9 +++++++++ arch/arm/boot/dts/exynos4412-odroidx.dts | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts b/arch/arm/boot/dts/exynos4412-odroidu3.dts index fa21981253af0f..90ae684bf60104 100644 --- a/arch/arm/boot/dts/exynos4412-odroidu3.dts +++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts @@ -112,6 +112,15 @@ &max77686 { voltage-regulators { + ldo22_reg: ldo@22 { + regulator-compatible = "LDO22"; + regulator-name = "vddf_emmc_2.85V"; + regulator-min-microvolt = <2850000>; + regulator-max-microvolt = <2850000>; + regulator-always-on; + regulator-boot-on; + }; + /* On the Odroid-U2/U3 this buck regulator supplies the LAN9730 block. */ buck8_reg: buck@8 { regulator-compatible = "BUCK8"; diff --git a/arch/arm/boot/dts/exynos4412-odroidx.dts b/arch/arm/boot/dts/exynos4412-odroidx.dts index 67c7a01066d8df..9f59e55f379f82 100644 --- a/arch/arm/boot/dts/exynos4412-odroidx.dts +++ b/arch/arm/boot/dts/exynos4412-odroidx.dts @@ -73,6 +73,13 @@ &max77686 { voltage-regulators { + ldo22_reg: ldo@22 { + regulator-compatible = "LDO22"; + regulator-name = "unused_2.8V"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + }; + /* On the Odroid-X2 this buck regulator supplies the core of the eMMC. */ buck8_reg: buck@8 { regulator-compatible = "BUCK8"; From a25fdbcf9a90c5aff5c0a172bfe7e2ae54267dcc Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Thu, 4 Sep 2014 01:11:15 +0200 Subject: [PATCH 727/788] ARM: dts: exynos4412-odroid: add usb3503 GPIO wait time This makes detection of devices attached to the usb3503 more reliable. --- arch/arm/boot/dts/exynos4412-odroid-common.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index 03518d5890a8d9..f7cebdd2a2a95f 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -204,6 +204,7 @@ connect-gpios = <&gpx3 4 0>; reset-gpios = <&gpx3 5 0>; initial-mode = <1>; + usb3503-gpio-waittime = <100>; }; max77686: pmic@09 { From b74019e41ef1d19292a412a175e91eb7b002cb56 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Thu, 4 Sep 2014 01:13:13 +0200 Subject: [PATCH 728/788] ARM: dts: exynos4412-odroid: fix eMMC regulator supplies The buck8 regulator is vmmc, ldo20 the vqmmc one. TODO: Probably only correct for the Odroid-X2. --- arch/arm/boot/dts/exynos4412-odroid-common.dtsi | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index f7cebdd2a2a95f..ef5ec8ece45117 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -96,7 +96,8 @@ mmc@12550000 { pinctrl-0 = <&sd4_clk &sd4_cmd &sd4_bus4 &sd4_bus8>; pinctrl-names = "default"; - vmmc-supply = <&ldo20_reg &buck8_reg>; + vmmc-supply = <&buck8_reg>; + vqmmc-supply = <&ldo20_reg>; status = "okay"; num-slots = <1>; From b74ee89c8e60070b3e14869d23cdb59965621466 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Thu, 4 Sep 2014 01:17:07 +0200 Subject: [PATCH 729/788] ARM: dts: exynos4412-odroid: fix SDHC regulator supplies The ldo21 regulator is vmmc, ldo4 the vqmmc one. TODO: Probably only correct for the Odroid-X2. --- arch/arm/boot/dts/exynos4412-odroid-common.dtsi | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index ef5ec8ece45117..1a61c0ad47bd7c 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -164,7 +164,8 @@ bus-width = <4>; pinctrl-0 = <&sd2_clk &sd2_cmd &sd2_cd &sd2_bus4>; pinctrl-names = "default"; - vmmc-supply = <&ldo4_reg &ldo21_reg>; + vmmc-supply = <&ldo21_reg>; + vqmmc-supply = <&ldo4_reg>; cd-gpios = <&gpk2 2 0>; cd-inverted; status = "okay"; From e984983be062889d9ceded066fca701a606b4fcb Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Thu, 4 Sep 2014 01:23:13 +0200 Subject: [PATCH 730/788] ARM: dts: exynos4412-odroid: remove always-on for ldo{4,21} Both LDO4 and LDO21 belong to the eMMC/SDHC subsystem. The vmmc/vqmmc supply entries should make sure that this regulators are properly enabled when needed. --- arch/arm/boot/dts/exynos4412-odroid-common.dtsi | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index 1a61c0ad47bd7c..f856bfb1641672 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -251,7 +251,6 @@ regulator-name = "vddq_mmc2_2.8V"; regulator-min-microvolt = <2800000>; regulator-max-microvolt = <2800000>; - regulator-always-on; regulator-boot-on; }; @@ -365,7 +364,6 @@ regulator-name = "tflash_2.8V"; regulator-min-microvolt = <2800000>; regulator-max-microvolt = <2800000>; - regulator-always-on; regulator-boot-on; }; From 19f6c52ba757dafa41afe665b477173f13c81e21 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Fri, 5 Sep 2014 06:24:37 +0200 Subject: [PATCH 731/788] ARM: dts: exynos4412-odroid: remove redundant pinctrl settings The pinctrl settings in i2c_0 and i2c_1 are already provided through the exynos4 dtsi. --- arch/arm/boot/dts/exynos4412-odroid-common.dtsi | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index f856bfb1641672..80d2fae01c2cab 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -192,8 +192,6 @@ }; i2c@13860000 { - pinctrl-0 = <&i2c0_bus>; - pinctrl-names = "default"; samsung,i2c-sda-delay = <100>; samsung,i2c-max-bus-freq = <400000>; status = "okay"; @@ -445,8 +443,6 @@ }; i2c@13870000 { - pinctrl-names = "default"; - pinctrl-0 = <&i2c1_bus>; status = "okay"; max98090: max98090@10 { compatible = "maxim,max98090"; From 8aea22937c160867156cd778d4ad50f94adf0b2a Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Fri, 5 Sep 2014 06:26:23 +0200 Subject: [PATCH 732/788] ARM: dts: exynos4412-odroid: add slave-addr to i2c_0 --- arch/arm/boot/dts/exynos4412-odroid-common.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index 80d2fae01c2cab..2a8d53ddf87031 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -192,6 +192,7 @@ }; i2c@13860000 { + samsung,i2c-slave-addr = <0x10>; samsung,i2c-sda-delay = <100>; samsung,i2c-max-bus-freq = <400000>; status = "okay"; From 61077ef975eba59e881b5a135fa3e66e8d69f5c3 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Fri, 5 Sep 2014 06:32:28 +0200 Subject: [PATCH 733/788] ARM: dts: exynos4412-odroid: tune settings for i2c_1 --- arch/arm/boot/dts/exynos4412-odroid-common.dtsi | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index 2a8d53ddf87031..27f42da55f3a54 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -445,6 +445,11 @@ i2c@13870000 { status = "okay"; + + samsung,i2c-slave-addr = <0x10>; + samsung,i2c-sda-delay = <100>; + samsung,i2c-max-bus-freq = <400000>; + max98090: max98090@10 { compatible = "maxim,max98090"; reg = <0x10>; From 83bb5bf9efde81bc60f1501ccc40a70e0e668074 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Wed, 24 Sep 2014 12:44:35 +0200 Subject: [PATCH 734/788] ARM: dts: exynos4412-odroidu3: specify buck8 as ext-regulator for usb3503 --- arch/arm/boot/dts/exynos4412-odroidu3.dts | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts b/arch/arm/boot/dts/exynos4412-odroidu3.dts index 90ae684bf60104..28f0b82407c48c 100644 --- a/arch/arm/boot/dts/exynos4412-odroidu3.dts +++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts @@ -108,6 +108,7 @@ clock-names = "refclk"; clocks = <&pmu_system_controller 0>; refclk-frequency = <24000000>; + ext-supply = <&buck8_reg>; }; &max77686 { From 4ddbf17978bb2d690358115b848dfb01dd845a21 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Wed, 24 Sep 2014 12:56:14 +0200 Subject: [PATCH 735/788] ARM: dts: exynos4412-odroid: move eMMC vmmc supply out of common The supply setup for the eMMC on the Odroid-{U2,X2} is different. The X2 uses buck8 as vmmc-supply, while the U2 uses ldo22. --- arch/arm/boot/dts/exynos4412-odroid-common.dtsi | 3 +-- arch/arm/boot/dts/exynos4412-odroidu3.dts | 3 +++ arch/arm/boot/dts/exynos4412-odroidx.dts | 4 ++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index 27f42da55f3a54..8f830addab1b15 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -93,10 +93,9 @@ }; }; - mmc@12550000 { + emmc: mmc@12550000 { pinctrl-0 = <&sd4_clk &sd4_cmd &sd4_bus4 &sd4_bus8>; pinctrl-names = "default"; - vmmc-supply = <&buck8_reg>; vqmmc-supply = <&ldo20_reg>; status = "okay"; diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts b/arch/arm/boot/dts/exynos4412-odroidu3.dts index 28f0b82407c48c..70310153cda2a2 100644 --- a/arch/arm/boot/dts/exynos4412-odroidu3.dts +++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts @@ -103,6 +103,9 @@ status = "okay"; }; +&emmc { + vmmc-supply = <&ldo22_reg>; +}; &usb3503 { clock-names = "refclk"; diff --git a/arch/arm/boot/dts/exynos4412-odroidx.dts b/arch/arm/boot/dts/exynos4412-odroidx.dts index 9f59e55f379f82..cd6cbeec06a2ca 100644 --- a/arch/arm/boot/dts/exynos4412-odroidx.dts +++ b/arch/arm/boot/dts/exynos4412-odroidx.dts @@ -71,6 +71,10 @@ }; }; +&emmc { + vmmc-supply = <&buck8_reg>; +}; + &max77686 { voltage-regulators { ldo22_reg: ldo@22 { From b4409e166bb1f4b0db53a9b0972f943b916a88d6 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Wed, 22 Oct 2014 02:08:23 +0200 Subject: [PATCH 736/788] ARM: dts: exynos4412-odroid: add more regulator nodes TODO: Probably most of these nodes are specific to the Odroid-X/X2. --- .../boot/dts/exynos4412-odroid-common.dtsi | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index 8f830addab1b15..2a4b3832e2abc4 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -286,6 +286,14 @@ regulator-max-microvolt = <1000000>; }; + ldo9_reg: ldo@9 { + regulator-compatible = "LDO9"; + regulator-name = "vt_core_1.0V"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + }; + /* This regulator also supplies the TMU block. */ ldo10_reg: ldo@10 { regulator-compatible = "LDO10"; @@ -347,6 +355,28 @@ regulator-boot-on; }; + ldo17_reg: ldo@17 { + regulator-compatible = "LDO17"; + regulator-name = "vddq_cam_1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + ldo18_reg: ldo@18 { + regulator-compatible = "LDO18"; + regulator-name = "vddq_isp_1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + ldo19_reg: ldo@19 { + regulator-compatible = "LDO19"; + regulator-name = "vt_cam_1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + /* Supply for IO of eMMC. */ ldo20_reg: ldo@20 { regulator-compatible = "LDO20"; @@ -365,6 +395,20 @@ regulator-boot-on; }; + ldo23_reg: ldo@23 { + regulator-compatible = "LDO23"; + regulator-name = "vdd_touch_2.8V"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + }; + + ldo24_reg: ldo@24 { + regulator-compatible = "LDO24"; + regulator-name = "vdd_touchled_3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + ldo25_reg: ldo@25 { regulator-compatible = "LDO25"; regulator-name = "vddq_lcd_1.8V"; @@ -374,6 +418,13 @@ regulator-boot-on; }; + ldo26_reg: ldo@26 { + regulator-compatible = "LDO26"; + regulator-name = "vdd_motor_3.0V"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + }; + buck1_reg: buck@1 { regulator-compatible = "BUCK1"; regulator-name = "vdd_mif"; @@ -438,6 +489,14 @@ regulator-max-microvolt = <2000000>; regulator-always-on; }; + + buck9_reg: buck@9 { + regulator-compatible = "BUCK9"; + regulator-name = "io_1.2V"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + }; }; }; }; From 1e0e789745025c46320892af2c68a9778a29d139 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Wed, 22 Oct 2014 02:25:27 +0200 Subject: [PATCH 737/788] ARM: dts: exynos4412-odroid: add regulator topology information TODO: Check with Hardkernel hardware team if the supply routing info is correct. --- .../boot/dts/exynos4412-odroid-common.dtsi | 59 +++++++++++++++++++ arch/arm/boot/dts/exynos4412-odroidu3.dts | 2 + arch/arm/boot/dts/exynos4412-odroidx.dts | 2 + 3 files changed, 63 insertions(+) diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index 2a4b3832e2abc4..dfe9cb981604dc 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -55,6 +55,34 @@ }; }; + regulators { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + reg_sysvdd: regulator@0 { + compatible = "regulator-fixed"; + reg = <0x0>; + regulator-name = "sysvdd"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + regulator-boot-on; + }; + + reg_p3v3: regulator@1 { + compatible = "regulator-fixed"; + reg = <0x1>; + regulator-name = "p3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpa1 1 3>; + enable-active-high; + regulator-always-on; + regulator-boot-on; + }; + }; + i2s0: i2s@03830000 { pinctrl-0 = <&i2s0_bus>; pinctrl-names = "default"; @@ -223,6 +251,7 @@ regulator-min-microvolt = <1000000>; regulator-max-microvolt = <1000000>; regulator-always-on; + LDO1-supply = <&buck6_reg>; }; ldo2_reg: ldo@2 { @@ -241,6 +270,7 @@ regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; regulator-always-on; + LDO3-supply = <&buck7_reg>; }; /* Supply for IO of SDHC. */ @@ -250,6 +280,7 @@ regulator-min-microvolt = <2800000>; regulator-max-microvolt = <2800000>; regulator-boot-on; + LDO4-supply = <®_sysvdd>; }; /* The LDO5 regulator provides reference voltage for the whole MMC * @@ -261,6 +292,7 @@ regulator-max-microvolt = <1800000>; regulator-always-on; regulator-boot-on; + LDO5-supply = <&buck7_reg>; }; ldo6_reg: ldo@6 { @@ -269,6 +301,7 @@ regulator-min-microvolt = <1000000>; regulator-max-microvolt = <1000000>; regulator-always-on; + LDO6-supply = <&buck6_reg>; }; ldo7_reg: ldo@7 { @@ -277,6 +310,7 @@ regulator-min-microvolt = <1000000>; regulator-max-microvolt = <1000000>; regulator-always-on; + LDO7-supply = <&buck6_reg>; }; ldo8_reg: ldo@8 { @@ -284,6 +318,7 @@ regulator-name = "vdd_hdmi_1.0V"; regulator-min-microvolt = <1000000>; regulator-max-microvolt = <1000000>; + LDO8-supply = <&buck6_reg>; }; ldo9_reg: ldo@9 { @@ -292,6 +327,7 @@ regulator-min-microvolt = <1000000>; regulator-max-microvolt = <1000000>; regulator-always-on; + LDO9-supply = <&buck7_reg>; }; /* This regulator also supplies the TMU block. */ @@ -300,6 +336,7 @@ regulator-name = "vddq_mipihsi_1.8V"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; + LDO10-supply = <&buck7_reg>; }; ldo11_reg: ldo@11 { @@ -308,6 +345,7 @@ regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; regulator-always-on; + LDO11-supply = <&buck7_reg>; }; ldo12_reg: ldo@12 { @@ -317,6 +355,7 @@ regulator-max-microvolt = <3300000>; regulator-always-on; regulator-boot-on; + LDO12-supply = <®_sysvdd>; }; ldo13_reg: ldo@13 { @@ -326,6 +365,7 @@ regulator-max-microvolt = <1800000>; regulator-always-on; regulator-boot-on; + LDO13-supply = <&buck7_reg>; }; ldo14_reg: ldo@14 { @@ -335,6 +375,7 @@ regulator-max-microvolt = <1800000>; regulator-always-on; regulator-boot-on; + LDO14-supply = <&buck7_reg>; }; ldo15_reg: ldo@15 { @@ -344,6 +385,7 @@ regulator-max-microvolt = <1000000>; regulator-always-on; regulator-boot-on; + LDO15-supply = <&buck6_reg>; }; ldo16_reg: ldo@16 { @@ -353,6 +395,7 @@ regulator-max-microvolt = <1800000>; regulator-always-on; regulator-boot-on; + LDO16-supply = <&buck7_reg>; }; ldo17_reg: ldo@17 { @@ -360,6 +403,7 @@ regulator-name = "vddq_cam_1.8V"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; + LDO17-supply = <&buck7_reg>; }; ldo18_reg: ldo@18 { @@ -368,6 +412,7 @@ regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; regulator-always-on; + LDO18-supply = <&buck7_reg>; }; ldo19_reg: ldo@19 { @@ -375,6 +420,7 @@ regulator-name = "vt_cam_1.8V"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; + LDO19-supply = <&buck7_reg>; }; /* Supply for IO of eMMC. */ @@ -384,6 +430,7 @@ regulator-min-microvolt = <1800000>; regulator-max-microvolt = <3000000>; regulator-boot-on; + LDO20-supply = <&buck7_reg>; }; /* Supply for core of SDHC. */ @@ -393,6 +440,7 @@ regulator-min-microvolt = <2800000>; regulator-max-microvolt = <2800000>; regulator-boot-on; + LDO21-supply = <®_sysvdd>; }; ldo23_reg: ldo@23 { @@ -400,6 +448,7 @@ regulator-name = "vdd_touch_2.8V"; regulator-min-microvolt = <2800000>; regulator-max-microvolt = <2800000>; + LDO23-supply = <®_sysvdd>; }; ldo24_reg: ldo@24 { @@ -407,6 +456,7 @@ regulator-name = "vdd_touchled_3.3V"; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; + LDO24-supply = <®_sysvdd>; }; ldo25_reg: ldo@25 { @@ -416,6 +466,7 @@ regulator-max-microvolt = <1800000>; regulator-always-on; regulator-boot-on; + LDO25-supply = <®_sysvdd>; }; ldo26_reg: ldo@26 { @@ -432,6 +483,7 @@ regulator-max-microvolt = <1100000>; regulator-always-on; regulator-boot-on; + BUCK1-supply = <®_sysvdd>; }; buck2_reg: buck@2 { @@ -441,6 +493,7 @@ regulator-max-microvolt = <1400000>; regulator-always-on; regulator-boot-on; + BUCK2-supply = <®_sysvdd>; }; buck3_reg: buck@3 { @@ -450,6 +503,7 @@ regulator-max-microvolt = <1125000>; regulator-always-on; regulator-boot-on; + BUCK3-supply = <®_sysvdd>; }; buck4_reg: buck@4 { @@ -458,6 +512,7 @@ regulator-min-microvolt = <850000>; regulator-max-microvolt = <1200000>; regulator-microvolt-offset = <50000>; + BUCK4-supply = <®_sysvdd>; }; /* This regulator also supplies: * @@ -469,6 +524,7 @@ regulator-max-microvolt = <1200000>; regulator-always-on; regulator-boot-on; + BUCK5-supply = <®_sysvdd>; }; /* Input to LDO: 1, 6, 7, 8 and 15 */ @@ -479,6 +535,7 @@ regulator-max-microvolt = <1350000>; regulator-always-on; regulator-boot-on; + BUCK6-supply = <®_sysvdd>; }; /* Input to LDO: 3, 5, 9, 11 and 17~20 */ @@ -488,6 +545,7 @@ regulator-min-microvolt = <2000000>; regulator-max-microvolt = <2000000>; regulator-always-on; + BUCK7-supply = <®_sysvdd>; }; buck9_reg: buck@9 { @@ -496,6 +554,7 @@ regulator-min-microvolt = <1200000>; regulator-max-microvolt = <1200000>; regulator-always-on; + BUCK9-supply = <®_sysvdd>; }; }; }; diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts b/arch/arm/boot/dts/exynos4412-odroidu3.dts index 70310153cda2a2..2e7ad08f3219bf 100644 --- a/arch/arm/boot/dts/exynos4412-odroidu3.dts +++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts @@ -123,6 +123,7 @@ regulator-max-microvolt = <2850000>; regulator-always-on; regulator-boot-on; + LDO22-supply = <®_sysvdd>; }; /* On the Odroid-U2/U3 this buck regulator supplies the LAN9730 block. */ @@ -131,6 +132,7 @@ regulator-name = "input_lan9730_3.3V"; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; + BUCK8-supply = <®_sysvdd ®_p3v3>; }; }; }; diff --git a/arch/arm/boot/dts/exynos4412-odroidx.dts b/arch/arm/boot/dts/exynos4412-odroidx.dts index cd6cbeec06a2ca..997430705dc81e 100644 --- a/arch/arm/boot/dts/exynos4412-odroidx.dts +++ b/arch/arm/boot/dts/exynos4412-odroidx.dts @@ -82,6 +82,7 @@ regulator-name = "unused_2.8V"; regulator-min-microvolt = <2800000>; regulator-max-microvolt = <2800000>; + LDO22-supply = <®_sysvdd>; }; /* On the Odroid-X2 this buck regulator supplies the core of the eMMC. */ @@ -90,6 +91,7 @@ regulator-name = "vddf_emmc_2.85V"; regulator-min-microvolt = <2850000>; regulator-max-microvolt = <2850000>; + BUCK8-supply = <®_sysvdd ®_p3v3>; }; }; }; From 90d1e882c621cb95673148d2eb80589040f3cc5b Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Wed, 22 Oct 2014 02:38:04 +0200 Subject: [PATCH 738/788] ARM: dts: exynos4412-odroidx2: add FIMD and related nodes --- arch/arm/boot/dts/exynos4412-odroidx2.dts | 45 +++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/arch/arm/boot/dts/exynos4412-odroidx2.dts b/arch/arm/boot/dts/exynos4412-odroidx2.dts index 6e33678562aebf..968b330b8ea3f1 100644 --- a/arch/arm/boot/dts/exynos4412-odroidx2.dts +++ b/arch/arm/boot/dts/exynos4412-odroidx2.dts @@ -12,6 +12,7 @@ */ #include "exynos4412-odroidx.dts" +#include / { model = "Hardkernel ODROID-X2 board based on Exynos4412"; @@ -20,6 +21,43 @@ memory { reg = <0x40000000 0x7FF00000>; }; + + pwm: pwm@139D0000 { + status = "okay"; + samsung,pwm-outputs = <0>, <1>; + }; + + display-timings { + native-mode = <&timing0>; + + timing0: timing { + clock-frequency = <500000>; + hactive = <1366>; + vactive = <768>; + hfront-porch = <48>; + hback-porch = <80>; + hsync-len = <32>; + vback-porch = <14>; + vfront-porch = <3>; + vsync-len = <5>; + pixelclk-active = <1>; + hsync-active = <1>; + vsync-active = <1>; + de-active = <0>; + }; + }; + + backlight { + compatible = "pwm-backlight"; + pwms = <&pwm 1 1000000 PWM_POLARITY_INVERTED>; + pwm-names = "backlight"; + + brightness-levels = <0 4 8 16 32 64 128 255>; + default-brightness-level = <7>; + + pinctrl-0 = <&pwm1_out>; + pinctrl-names = "default"; + }; }; &sound { @@ -34,3 +72,10 @@ "IN1", "Mic Jack", "Mic Jack", "MICBIAS"; }; + +&fimd { + status = "okay"; + + pinctrl-0 = <&lcd_clk &lcd_data24 &pwm1_out>; + pinctrl-names = "default"; +}; From b21ff7a6d8a5be7c0d57ad4ea8d3c6086fe16add Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sun, 15 Feb 2015 00:53:02 +0100 Subject: [PATCH 739/788] ARM: dts: odroid: adjust vdd_{mif,int} voltage ranges This is necessary for exynos-busfreq to regulate MIF and INT voltage. --- arch/arm/boot/dts/exynos4412-odroid-common.dtsi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index dfe9cb981604dc..05695b74868744 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -479,7 +479,7 @@ buck1_reg: buck@1 { regulator-compatible = "BUCK1"; regulator-name = "vdd_mif"; - regulator-min-microvolt = <1100000>; + regulator-min-microvolt = <850000>; regulator-max-microvolt = <1100000>; regulator-always-on; regulator-boot-on; @@ -499,8 +499,8 @@ buck3_reg: buck@3 { regulator-compatible = "BUCK3"; regulator-name = "vdd_int"; - regulator-min-microvolt = <1125000>; - regulator-max-microvolt = <1125000>; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <1150000>; regulator-always-on; regulator-boot-on; BUCK3-supply = <®_sysvdd>; From 8f5be2b2b2d9175eecd1472a110234964788bd43 Mon Sep 17 00:00:00 2001 From: Alban Browaeys Date: Thu, 3 Oct 2013 20:19:44 +0000 Subject: [PATCH 740/788] HACK: mmc: dw_mmc-exynos: implementing eMMC hwreset on restart Some systems need a hardware reset of the eMMC subsystem on system restart to make sure that the eMMC is useable once the systems comes up again. Implement this by triggering the reset line through a GPIO. --- .../bindings/mmc/exynos-dw-mshc.txt | 5 ++++ drivers/mmc/host/dw_mmc-exynos.c | 29 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt index ee4fc0576c7d86..48f0ff9836bfed 100644 --- a/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt +++ b/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt @@ -50,6 +50,11 @@ Required Properties: - if CIU clock divider value is 0 (that is divide by 1), both tx and rx phase shift clocks should be 0. +Optional properties: + +* samsung,dw-mshc-hwreset-gpio: GPIO specification for a eMMC reset line which + is triggered on system restart. + Required properties for a slot (Deprecated - Recommend to use one slot per host): * gpios: specifies a list of gpios used for command, clock and data bus. The diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index d0a3b9a9bef098..a27a811f853d12 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "dw_mmc.h" #include "dw_mmc-pltfm.h" @@ -77,6 +78,7 @@ struct dw_mci_exynos_priv_data { u32 sdr_timing; u32 ddr_timing; u32 cur_speed; + int hwreset_gpio; }; static struct dw_mci_exynos_compatible { @@ -295,7 +297,10 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host) return ret; priv->ddr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div); + + priv->hwreset_gpio = of_get_named_gpio(np, "samsung,dw-mshc-hwreset-gpio", 0); host->priv = priv; + return 0; } @@ -480,6 +485,29 @@ static const struct of_device_id dw_mci_exynos_match[] = { }; MODULE_DEVICE_TABLE(of, dw_mci_exynos_match); + +static void dw_mci_exynos_shutdown(struct platform_device *pdev) +{ + struct dw_mci *host = platform_get_drvdata(pdev); + int gpio; + + if (host && host->priv) { + struct dw_mci_exynos_priv_data *priv = host->priv; + gpio = priv->hwreset_gpio; + } + + if (system_state != SYSTEM_RESTART || !gpio_is_valid(gpio)) + return; + + /* Issue eMMC HW_RST */ + gpio_request(gpio, "GPK1"); + gpio_direction_output(gpio, 0); + msleep(150); + gpio_direction_output(gpio, 1); + gpio_free(gpio); + msleep(150); +} + static int dw_mci_exynos_probe(struct platform_device *pdev) { const struct dw_mci_drv_data *drv_data; @@ -500,6 +528,7 @@ static const struct dev_pm_ops dw_mci_exynos_pmops = { static struct platform_driver dw_mci_exynos_pltfm_driver = { .probe = dw_mci_exynos_probe, .remove = dw_mci_pltfm_remove, + .shutdown = dw_mci_exynos_shutdown, .driver = { .name = "dwmmc_exynos", .of_match_table = dw_mci_exynos_match, From e4887eab976a22f3202827dcab8ff1268257f040 Mon Sep 17 00:00:00 2001 From: Alban Browaeys Date: Thu, 18 Sep 2014 15:07:39 +0200 Subject: [PATCH 741/788] HACK: ARM: dts: exynos4412-odroid: add eMMC hwreset GPIO The eMMC subsystem of the Exynos4412-based Odroid-X2/U2/U3 boards needs a hardware reset on system restart to properly detect the eMMC again after the restart. --- arch/arm/boot/dts/exynos4412-odroid-common.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index 05695b74868744..0d67340f3e9858 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -133,6 +133,7 @@ samsung,dw-mshc-ciu-div = <3>; samsung,dw-mshc-sdr-timing = <2 3>; samsung,dw-mshc-ddr-timing = <1 2>; + samsung,dw-mshc-hwreset-gpio = <&gpk1 2 1>; bus-width = <8>; cap-mmc-highspeed; }; From eb81ae996dffb9c0da17d35297db5341603af42d Mon Sep 17 00:00:00 2001 From: Alban Browaeys Date: Sun, 12 Oct 2014 22:19:20 +0200 Subject: [PATCH 742/788] mmc: sdhci: don't set clock while holding spinlock Based on a comment from Ulf Hansson I unlock_irqrestore the spinlocks prior to the 'set_clock' callbacks. Bug initially reported by Daniel Drake as: "sdhci_s3c_consider_clock scheduling while atomic - clk_round_rate" on the linux-mmc mailing list. --- drivers/mmc/host/sdhci.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index f1a488ee432f89..ec230428555756 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1520,7 +1520,9 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) sdhci_enable_preset_value(host, false); if (!ios->clock || ios->clock != host->clock) { + spin_unlock_irqrestore(&host->lock, flags); host->ops->set_clock(host, ios->clock); + spin_lock_irqsave(&host->lock, flags); host->clock = ios->clock; if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK && @@ -1595,7 +1597,9 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); /* Re-enable SD Clock */ + spin_unlock_irqrestore(&host->lock, flags); host->ops->set_clock(host, host->clock); + spin_lock_irqsave(&host->lock, flags); } /* Reset SD Clock Enable */ @@ -1621,7 +1625,9 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) } /* Re-enable SD Clock */ + spin_unlock_irqrestore(&host->lock, flags); host->ops->set_clock(host, host->clock); + spin_lock_irqsave(&host->lock, flags); } else sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); From 7265124d4f99c412d2d16d783ae2e84b14ee096b Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sat, 19 Apr 2014 21:35:34 +0200 Subject: [PATCH 743/788] README: add mini-docu describing this tree --- README-ODROID.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 README-ODROID.md diff --git a/README-ODROID.md b/README-ODROID.md new file mode 100644 index 00000000000000..dcb92be7fa23c8 --- /dev/null +++ b/README-ODROID.md @@ -0,0 +1,17 @@ +# Vanilla Linux 3.19.y for the Odroid-X2/U2/U3 + +This is a [linux-3.19.y](https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/log/?h=linux-3.19.y) tree with some modifications to make it work on a Hardkernel Odroid-X2 developer board. The U2/U3 boards should work as well, but are neither owned nor tested by me. + + +TODOs: + + - Mali code is merged, but still needs large amount of patching (DRM side mostly) + - remove more of the always-on properties of the various regulators + - modify refclk code in usb3503 (make it more generic) + +EXTERNAL TODOs: + + - DRM runtime PM currently not functional when IOMMU is used + - virtual hwmon device for thermal zone not created + - FIMC probe errors on boot + - loading MFC locks up system (investigate) From 5f99a6286b7c9088f137a6ba59f097c62a11bce7 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Wed, 1 Oct 2014 16:22:09 -0600 Subject: [PATCH 744/788] drm/exynos: enable QoS to avoid visual glitches at high resolutions At the 800MHz MPLL / 400MHz memory bus / 200MHz AXI bus configuration, the Exynos4412 is unable to reliably scan out a 1080p@60Hz image over HDMI when the GPU is in use. Enable some QoS functionality (or rather, simple GPU slowdown) to avoid this problem. And do the same for VGA for the display modes where this problem can be seen there. --- arch/arm/mach-exynos/exynos.c | 44 +++++++++++++++++++++++++ drivers/gpu/drm/exynos/exynos_drm_dpi.c | 17 ++++++++++ drivers/gpu/drm/exynos/exynos_drm_drv.h | 2 ++ drivers/gpu/drm/exynos/exynos_hdmi.c | 15 +++++++++ 4 files changed, 78 insertions(+) diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c index dce79f614c6f48..93ad410ed03fb0 100644 --- a/arch/arm/mach-exynos/exynos.c +++ b/arch/arm/mach-exynos/exynos.c @@ -309,6 +309,50 @@ static void __init exynos_dt_fixup(void) of_fdt_limit_memory(8); } +/* Undocumented Exynos4412 QoS registers. Needed to prioritize RAM accesses + * from display controllers in the face of bus competition. + * In particular, we have seen that the GPU can easily saturate memory + * bandwidth and cause underflows for the display controllers (visual glitches + * appear at 1080p@60Hz). + * + * There is a higher-than-expected GPU performance degradation when this QoS + * is applied. And actually, testing at 640x480 where bus competition should + * be minimal, the GPU performance loss is still equal. Therefore this does + * not seem to be working as QoS, instead it just makes the GPU slow down to + * the point where it can't saturate memory bandwidth. Sigh. + * Anyway, it avoids the glitches. + * + * The best documentation of these registers can be found from an old Android + * code drop, as below: + * + * QOSAC_GDL (0x11600400) + * [0] Image Block: "1" Full Resource, "0" Tidemark + * [1] TV Block: "1" Full Resource, "0" Tidemark + * [2] G3D Block: "1" Full Resource, "0" Tidemark + * [3] MFC_L Block: "1" Full Resource, "0" Tidemark + * + * QOSAC_GDR (0x11200400) + * [0] CAM Block: "1" Full Resource, "0" Tidemark + * [1] LCD0 Block: "1" Full Resource, "0" Tidemark + * [2] LCD1 Block: "1" Full Resource, "0" Tidemark + * [3] FSYS Block: "1" Full Resource, "0" Tidemark + * [4] MFC_R Block: "1" Full Resource, "0" Tidemark + * [5] MAUDIO Block: "1" Full Resource, "0" Tidemark + * + * We have no docs for the AC registers (offset +4 from the above). + * We currently only write to the left bus registers. +*/ +void exynos4412_qos(u8 tm, u8 ac) +{ + void __iomem *qos_gdl_base = ioremap(0x11600400, 8); + if (!qos_gdl_base) + return; + + __raw_writel(tm, qos_gdl_base); + __raw_writel(ac, qos_gdl_base + 4); + iounmap(qos_gdl_base); +} + DT_MACHINE_START(EXYNOS_DT, "SAMSUNG EXYNOS (Flattened Device Tree)") /* Maintainer: Thomas Abraham */ /* Maintainer: Kukjin Kim */ diff --git a/drivers/gpu/drm/exynos/exynos_drm_dpi.c b/drivers/gpu/drm/exynos/exynos_drm_dpi.c index 37678cf4425ad5..0e0aad60377e00 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dpi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dpi.c @@ -166,7 +166,24 @@ static void exynos_dpi_dpms(struct exynos_drm_display *display, int mode) ctx->dpms_mode = mode; } +static void exynos_dpi_mode_set(struct exynos_drm_display *display, + struct drm_display_mode *mode) +{ + /* At 1280x1024@60Hz and higher there is not enough memory bandwidth + * available for the display controller when the GPU is busy. So we + * apply a "QoS" scheme. + * I found these numbers through guesswork. The GPU performance is + * degraded by about 30%, but there are no flickers. + */ + if (mode->clock >= 135000) + exynos4412_qos(3, 3); + else + exynos4412_qos(0, 0); +} + + static struct exynos_drm_display_ops exynos_dpi_display_ops = { + .mode_set = exynos_dpi_mode_set, .create_connector = exynos_dpi_create_connector, .dpms = exynos_dpi_dpms }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 2e5063488c5026..6e60faa29265ad 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -41,6 +41,8 @@ enum exynos_drm_output_type { EXYNOS_DISPLAY_TYPE_VIDI, }; +extern void exynos4412_qos(u8 tm, u8 ac); + /* * Exynos drm common overlay structure. * diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 98051e8e855a1f..e7cce510db942a 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -1661,6 +1661,21 @@ static void hdmi_v14_mode_apply(struct hdmi_context *hdata) static void hdmi_mode_apply(struct hdmi_context *hdata) { + struct drm_display_mode *m = &hdata->current_mode; + + /* At 1080p at 60Hz, there is not enough memory bandwidth available + * for the display controller when the GPU is busy. So we apply a + * "QoS" scheme. + * According to Samsung, this should result in image, G3D, and MFC + * having the same priority, and when bus competition occurs, the + * TV(HDMI) has the highest priority. + * GPU performance drops by about 10%. + */ + if (m->clock == 148500 && drm_mode_vrefresh(m) > 50) + exynos4412_qos(7, 2); + else + exynos4412_qos(0, 0); + if (hdata->type == HDMI_TYPE13) hdmi_v13_mode_apply(hdata); else From 3ee93f3c5efdebd352d4997b53fe53d8a5df4e9e Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sat, 31 Jan 2015 20:32:30 +0100 Subject: [PATCH 745/788] mali: fix usage of strict_strt{oul,ol} --- .../gpu/arm/mali/linux/mali_kernel_sysfs.c | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/arm/mali/linux/mali_kernel_sysfs.c b/drivers/gpu/arm/mali/linux/mali_kernel_sysfs.c index 026e2415ded2c4..200b25eaec98a7 100644 --- a/drivers/gpu/arm/mali/linux/mali_kernel_sysfs.c +++ b/drivers/gpu/arm/mali/linux/mali_kernel_sysfs.c @@ -118,7 +118,7 @@ static ssize_t group_enabled_write(struct file *filp, const char __user *buf, si } buffer[count] = '\0'; - r = strict_strtoul(&buffer[0], 10, &val); + r = kstrtoul(&buffer[0], 10, &val); if (0 != r) { return -EINVAL; } @@ -231,7 +231,7 @@ static ssize_t profiling_counter_src_write(struct file *filp, const char __user buf[cnt] = 0; - ret = strict_strtol(buf, 10, &val); + ret = kstrtol(buf, 10, &val); if (ret < 0) { return ret; } @@ -316,7 +316,7 @@ static ssize_t l2_l2x_counter_srcx_write(struct file *filp, const char __user *u buf[cnt] = 0; - ret = strict_strtol(buf, 10, &val); + ret = kstrtol(buf, 10, &val); if (ret < 0) { return ret; } @@ -354,7 +354,7 @@ static ssize_t l2_all_counter_srcx_write(struct file *filp, const char __user *u buf[cnt] = 0; - ret = strict_strtol(buf, 10, &val); + ret = kstrtol(buf, 10, &val); if (ret < 0) { return ret; } @@ -448,7 +448,7 @@ static ssize_t power_always_on_write(struct file *filp, const char __user *ubuf, } buf[cnt] = '\0'; - ret = strict_strtoul(buf, 10, &val); + ret = kstrtoul(buf, 10, &val); if (0 != ret) { return ret; } @@ -574,7 +574,7 @@ static ssize_t profiling_record_write(struct file *filp, const char __user *ubuf buf[cnt] = 0; - ret = strict_strtoul(buf, 10, &val); + ret = kstrtoul(buf, 10, &val); if (ret < 0) { return ret; } @@ -911,7 +911,7 @@ static ssize_t user_settings_write(struct file *filp, const char __user *ubuf, s } buf[cnt] = '\0'; - ret = strict_strtoul(buf, 10, &val); + ret = kstrtoul(buf, 10, &val); if (0 != ret) { return ret; } @@ -976,7 +976,7 @@ static ssize_t pmu_power_down_write(struct file *filp, const char __user *buf, s } buffer[count] = '\0'; - ret = strict_strtoul(&buffer[0], 10, &val); + ret = kstrtoul(&buffer[0], 10, &val); if (0 != ret) { return -EINVAL; } @@ -1010,7 +1010,7 @@ static ssize_t pmu_power_up_write(struct file *filp, const char __user *buf, siz } buffer[count] = '\0'; - ret = strict_strtoul(&buffer[0], 10, &val); + ret = kstrtoul(&buffer[0], 10, &val); if (0 != ret) { return -EINVAL; } @@ -1052,7 +1052,7 @@ static ssize_t pp_num_cores_enabled_write(struct file *filp, const char __user * } buffer[count] = '\0'; - ret = strict_strtoul(&buffer[0], 10, &val); + ret = kstrtoul(&buffer[0], 10, &val); if (0 != ret) { return -EINVAL; } @@ -1113,7 +1113,7 @@ static ssize_t pp_core_scaling_enabled_write(struct file *filp, const char __use } buffer[count] = '\0'; - ret = strict_strtoul(&buffer[0], 10, &val); + ret = kstrtoul(&buffer[0], 10, &val); if (0 != ret) { return -EINVAL; } From 60a4d6c8afa99f65e66a5ad9223867e296e64983 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sat, 31 Jan 2015 20:36:23 +0100 Subject: [PATCH 746/788] mali: remove _mali_osk_mem_check_allocated --- drivers/gpu/arm/mali/common/mali_osk.h | 16 ---------------- drivers/gpu/arm/mali/linux/mali_osk_memory.c | 8 -------- 2 files changed, 24 deletions(-) diff --git a/drivers/gpu/arm/mali/common/mali_osk.h b/drivers/gpu/arm/mali/common/mali_osk.h index 5a9cf36fbe223e..162482ba0850f1 100644 --- a/drivers/gpu/arm/mali/common/mali_osk.h +++ b/drivers/gpu/arm/mali/common/mali_osk.h @@ -520,22 +520,6 @@ void *_mali_osk_memset( void *s, u32 c, u32 n ); /** @} */ /* end group _mali_osk_memory */ -/** @brief Checks the amount of memory allocated - * - * Checks that not more than \a max_allocated bytes are allocated. - * - * Some OS bring up an interactive out of memory dialogue when the - * system runs out of memory. This can stall non-interactive - * apps (e.g. automated test runs). This function can be used to - * not trigger the OOM dialogue by keeping allocations - * within a certain limit. - * - * @return MALI_TRUE when \a max_allocated bytes are not in use yet. MALI_FALSE - * when at least \a max_allocated bytes are in use. - */ -mali_bool _mali_osk_mem_check_allocated( u32 max_allocated ); - - /** @addtogroup _mali_osk_low_level_memory * @{ */ diff --git a/drivers/gpu/arm/mali/linux/mali_osk_memory.c b/drivers/gpu/arm/mali/linux/mali_osk_memory.c index 64d8951709f0b3..eea9446a25de07 100644 --- a/drivers/gpu/arm/mali/linux/mali_osk_memory.c +++ b/drivers/gpu/arm/mali/linux/mali_osk_memory.c @@ -51,11 +51,3 @@ void inline *_mali_osk_memset( void *s, u32 c, u32 n ) { return memset(s, c, n); } - -mali_bool _mali_osk_mem_check_allocated( u32 max_allocated ) -{ - /* No need to prevent an out-of-memory dialogue appearing on Linux, - * so we always return MALI_TRUE. - */ - return MALI_TRUE; -} From 19544fe49bf6f95afddf3e5e11099041d2bee569 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sat, 31 Jan 2015 20:44:29 +0100 Subject: [PATCH 747/788] mali: remove mali_osk_memory source The mali_osk_memory source only contains some lightweight wrappers around standard kernel functions, so put these into the mali_osk header as static inline functions. --- drivers/gpu/arm/mali/Kbuild | 1 - drivers/gpu/arm/mali/common/mali_osk.h | 35 ++++++++++--- drivers/gpu/arm/mali/linux/mali_osk_memory.c | 53 -------------------- 3 files changed, 28 insertions(+), 61 deletions(-) delete mode 100644 drivers/gpu/arm/mali/linux/mali_osk_memory.c diff --git a/drivers/gpu/arm/mali/Kbuild b/drivers/gpu/arm/mali/Kbuild index c8da7a090b614d..121573de7b9c00 100644 --- a/drivers/gpu/arm/mali/Kbuild +++ b/drivers/gpu/arm/mali/Kbuild @@ -49,7 +49,6 @@ mali-y += \ linux/mali_osk_wait_queue.o \ linux/mali_osk_low_level_mem.o \ linux/mali_osk_math.o \ - linux/mali_osk_memory.o \ linux/mali_osk_misc.o \ linux/mali_osk_mali.o \ linux/mali_osk_notification.o \ diff --git a/drivers/gpu/arm/mali/common/mali_osk.h b/drivers/gpu/arm/mali/common/mali_osk.h index 162482ba0850f1..edc70d6b9e0086 100644 --- a/drivers/gpu/arm/mali/common/mali_osk.h +++ b/drivers/gpu/arm/mali/common/mali_osk.h @@ -407,7 +407,10 @@ u32 _mali_osk_atomic_xchg( _mali_osk_atomic_t *atom, u32 val ); * @param size Size of each element * @return On success, the zero-initialized buffer allocated. NULL on failure */ -void *_mali_osk_calloc( u32 n, u32 size ); +static inline void *_mali_osk_calloc( u32 n, u32 size ) +{ + return kcalloc(n, size, GFP_KERNEL); +} /** @brief Allocate memory. * @@ -433,7 +436,10 @@ void *_mali_osk_calloc( u32 n, u32 size ); * @param size Number of bytes to allocate * @return On success, the buffer allocated. NULL on failure. */ -void *_mali_osk_malloc( u32 size ); +static inline void *_mali_osk_malloc( u32 size ) +{ + return kmalloc(size, GFP_KERNEL); +} /** @brief Free memory. * @@ -449,7 +455,10 @@ void *_mali_osk_malloc( u32 size ); * * @param ptr Pointer to buffer to free */ -void _mali_osk_free( void *ptr ); +static inline void _mali_osk_free( void *ptr ) +{ + kfree(ptr); +} /** @brief Allocate memory. * @@ -474,7 +483,10 @@ void _mali_osk_free( void *ptr ); * @param size Number of bytes to allocate * @return On success, the buffer allocated. NULL on failure. */ -void *_mali_osk_valloc( u32 size ); +static inline void *_mali_osk_valloc( u32 size ) +{ + return vmalloc(size); +} /** @brief Free memory. * @@ -489,7 +501,10 @@ void *_mali_osk_valloc( u32 size ); * * @param ptr Pointer to buffer to free */ -void _mali_osk_vfree( void *ptr ); +static inline void _mali_osk_vfree( void *ptr ) +{ + vfree(ptr); +} /** @brief Copies memory. * @@ -504,7 +519,10 @@ void _mali_osk_vfree( void *ptr ); * @param len Number of bytes to copy. * @return \a dst is always passed through unmodified. */ -void *_mali_osk_memcpy( void *dst, const void *src, u32 len ); +static inline void *_mali_osk_memcpy( void *dst, const void *src, u32 len ) +{ + return memcpy(dst, src, len); +} /** @brief Fills memory. * @@ -516,7 +534,10 @@ void *_mali_osk_memcpy( void *dst, const void *src, u32 len ); * @param n Number of bytes to be set to the value. * @return \a s is always passed through unmodified */ -void *_mali_osk_memset( void *s, u32 c, u32 n ); +static inline void *_mali_osk_memset( void *s, u32 c, u32 n ) +{ + return memset(s, c, n); +} /** @} */ /* end group _mali_osk_memory */ diff --git a/drivers/gpu/arm/mali/linux/mali_osk_memory.c b/drivers/gpu/arm/mali/linux/mali_osk_memory.c deleted file mode 100644 index eea9446a25de07..00000000000000 --- a/drivers/gpu/arm/mali/linux/mali_osk_memory.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2010-2011, 2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** - * @file mali_osk_memory.c - * Implementation of the OS abstraction layer for the kernel device driver - */ - -#include "mali_osk.h" -#include -#include - -void inline *_mali_osk_calloc( u32 n, u32 size ) -{ - return kcalloc(n, size, GFP_KERNEL); -} - -void inline *_mali_osk_malloc( u32 size ) -{ - return kmalloc(size, GFP_KERNEL); -} - -void inline _mali_osk_free( void *ptr ) -{ - kfree(ptr); -} - -void inline *_mali_osk_valloc( u32 size ) -{ - return vmalloc(size); -} - -void inline _mali_osk_vfree( void *ptr ) -{ - vfree(ptr); -} - -void inline *_mali_osk_memcpy( void *dst, const void *src, u32 len ) -{ - return memcpy(dst, src, len); -} - -void inline *_mali_osk_memset( void *s, u32 c, u32 n ) -{ - return memset(s, c, n); -} From 5593270fdb05a25ccfa669910f26dd4a7932cd99 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sat, 31 Jan 2015 20:59:01 +0100 Subject: [PATCH 748/788] mali: remove mali_osk_atomics source Only contains wrapper code, so convert this to static inlines in the mali_osk header. Needs some additional moving around of defines. --- drivers/gpu/arm/mali/Kbuild | 1 - .../gpu/arm/mali/common/mali_kernel_common.h | 81 ------------ drivers/gpu/arm/mali/common/mali_osk.h | 124 ++++++++++++++++-- drivers/gpu/arm/mali/linux/mali_osk_atomics.c | 60 --------- 4 files changed, 116 insertions(+), 150 deletions(-) delete mode 100644 drivers/gpu/arm/mali/linux/mali_osk_atomics.c diff --git a/drivers/gpu/arm/mali/Kbuild b/drivers/gpu/arm/mali/Kbuild index 121573de7b9c00..5373d637b17e17 100644 --- a/drivers/gpu/arm/mali/Kbuild +++ b/drivers/gpu/arm/mali/Kbuild @@ -42,7 +42,6 @@ else endif mali-y += \ - linux/mali_osk_atomics.o \ linux/mali_osk_irq.o \ linux/mali_osk_wq.o \ linux/mali_osk_locks.o \ diff --git a/drivers/gpu/arm/mali/common/mali_kernel_common.h b/drivers/gpu/arm/mali/common/mali_kernel_common.h index fe57539fcda11c..2ef6cf18815ebf 100644 --- a/drivers/gpu/arm/mali/common/mali_kernel_common.h +++ b/drivers/gpu/arm/mali/common/mali_kernel_common.h @@ -38,87 +38,6 @@ * [5:6] Is messages with low priority, used during extensive debugging. */ -/** -* Fundamental error macro. Reports an error code. This is abstracted to allow us to -* easily switch to a different error reporting method if we want, and also to allow -* us to search for error returns easily. -* -* Note no closing semicolon - this is supplied in typical usage: -* -* MALI_ERROR(MALI_ERROR_OUT_OF_MEMORY); -*/ -#define MALI_ERROR(error_code) return (error_code) - -/** - * Basic error macro, to indicate success. - * Note no closing semicolon - this is supplied in typical usage: - * - * MALI_SUCCESS; - */ -#define MALI_SUCCESS MALI_ERROR(_MALI_OSK_ERR_OK) - -/** - * Basic error macro. This checks whether the given condition is true, and if not returns - * from this function with the supplied error code. This is a macro so that we can override it - * for stress testing. - * - * Note that this uses the do-while-0 wrapping to ensure that we don't get problems with dangling - * else clauses. Note also no closing semicolon - this is supplied in typical usage: - * - * MALI_CHECK((p!=NULL), ERROR_NO_OBJECT); - */ -#define MALI_CHECK(condition, error_code) do { if(!(condition)) MALI_ERROR(error_code); } while(0) - -/** - * Error propagation macro. If the expression given is anything other than _MALI_OSK_NO_ERROR, - * then the value is returned from the enclosing function as an error code. This effectively - * acts as a guard clause, and propagates error values up the call stack. This uses a - * temporary value to ensure that the error expression is not evaluated twice. - * If the counter for forcing a failure has been set using _mali_force_error, this error will be - * returned without evaluating the expression in MALI_CHECK_NO_ERROR - */ -#define MALI_CHECK_NO_ERROR(expression) \ - do { _mali_osk_errcode_t _check_no_error_result=(expression); \ - if(_check_no_error_result != _MALI_OSK_ERR_OK) \ - MALI_ERROR(_check_no_error_result); \ - } while(0) - -/** - * Pointer check macro. Checks non-null pointer. - */ -#define MALI_CHECK_NON_NULL(pointer, error_code) MALI_CHECK( ((pointer)!=NULL), (error_code) ) - -/** - * Error macro with goto. This checks whether the given condition is true, and if not jumps - * to the specified label using a goto. The label must therefore be local to the function in - * which this macro appears. This is most usually used to execute some clean-up code before - * exiting with a call to ERROR. - * - * Like the other macros, this is a macro to allow us to override the condition if we wish, - * e.g. to force an error during stress testing. - */ -#define MALI_CHECK_GOTO(condition, label) do { if(!(condition)) goto label; } while(0) - -/** - * Explicitly ignore a parameter passed into a function, to suppress compiler warnings. - * Should only be used with parameter names. - */ -#define MALI_IGNORE(x) x=x - -#define MALI_PRINTF(args) _mali_osk_dbgmsg args; - -#define MALI_PRINT_ERROR(args) do{ \ - MALI_PRINTF(("Mali: ERR: %s\n" ,__FILE__)); \ - MALI_PRINTF((" %s()%4d\n ", __FUNCTION__, __LINE__)) ; \ - MALI_PRINTF(args); \ - MALI_PRINTF(("\n")); \ - } while(0) - -#define MALI_PRINT(args) do{ \ - MALI_PRINTF(("Mali: ")); \ - MALI_PRINTF(args); \ - } while (0) - #ifdef DEBUG #ifndef mali_debug_level extern int mali_debug_level; diff --git a/drivers/gpu/arm/mali/common/mali_osk.h b/drivers/gpu/arm/mali/common/mali_osk.h index edc70d6b9e0086..e4598589381fa5 100644 --- a/drivers/gpu/arm/mali/common/mali_osk.h +++ b/drivers/gpu/arm/mali/common/mali_osk.h @@ -16,6 +16,88 @@ #ifndef __MALI_OSK_H__ #define __MALI_OSK_H__ +/** +* Fundamental error macro. Reports an error code. This is abstracted to allow us to +* easily switch to a different error reporting method if we want, and also to allow +* us to search for error returns easily. +* +* Note no closing semicolon - this is supplied in typical usage: +* +* MALI_ERROR(MALI_ERROR_OUT_OF_MEMORY); +*/ +#define MALI_ERROR(error_code) return (error_code) + +/** + * Basic error macro, to indicate success. + * Note no closing semicolon - this is supplied in typical usage: + * + * MALI_SUCCESS; + */ +#define MALI_SUCCESS MALI_ERROR(_MALI_OSK_ERR_OK) + +/** + * Basic error macro. This checks whether the given condition is true, and if not returns + * from this function with the supplied error code. This is a macro so that we can override it + * for stress testing. + * + * Note that this uses the do-while-0 wrapping to ensure that we don't get problems with dangling + * else clauses. Note also no closing semicolon - this is supplied in typical usage: + * + * MALI_CHECK((p!=NULL), ERROR_NO_OBJECT); + */ +#define MALI_CHECK(condition, error_code) do { if(!(condition)) MALI_ERROR(error_code); } while(0) + +/** + * Pointer check macro. Checks non-null pointer. + */ +#define MALI_CHECK_NON_NULL(pointer, error_code) MALI_CHECK( ((pointer)!=NULL), (error_code) ) + +/** + * Error propagation macro. If the expression given is anything other than _MALI_OSK_NO_ERROR, + * then the value is returned from the enclosing function as an error code. This effectively + * acts as a guard clause, and propagates error values up the call stack. This uses a + * temporary value to ensure that the error expression is not evaluated twice. + * If the counter for forcing a failure has been set using _mali_force_error, this error will be + * returned without evaluating the expression in MALI_CHECK_NO_ERROR + */ +#define MALI_CHECK_NO_ERROR(expression) \ + do { _mali_osk_errcode_t _check_no_error_result=(expression); \ + if(_check_no_error_result != _MALI_OSK_ERR_OK) \ + MALI_ERROR(_check_no_error_result); \ + } while(0) + +/** + * Error macro with goto. This checks whether the given condition is true, and if not jumps + * to the specified label using a goto. The label must therefore be local to the function in + * which this macro appears. This is most usually used to execute some clean-up code before + * exiting with a call to ERROR. + * + * Like the other macros, this is a macro to allow us to override the condition if we wish, + * e.g. to force an error during stress testing. + */ +#define MALI_CHECK_GOTO(condition, label) do { if(!(condition)) goto label; } while(0) + +/** + * Explicitly ignore a parameter passed into a function, to suppress compiler warnings. + * Should only be used with parameter names. + */ +#define MALI_IGNORE(x) x=x + +#define MALI_PRINTF(args) _mali_osk_dbgmsg args; + +#define MALI_PRINT_ERROR(args) do{ \ + MALI_PRINTF(("Mali: ERR: %s\n" ,__FILE__)); \ + MALI_PRINTF((" %s()%4d\n ", __FUNCTION__, __LINE__)) ; \ + MALI_PRINTF(args); \ + MALI_PRINTF(("\n")); \ + } while(0) + +#define MALI_PRINT(args) do{ \ + MALI_PRINTF(("Mali: ")); \ + MALI_PRINTF(args); \ + } while (0) + + #include "mali_osk_types.h" #include "mali_osk_specific.h" /* include any per-os specifics */ #include "mali_osk_locks.h" @@ -322,25 +404,37 @@ void _mali_osk_irq_term( _mali_osk_irq_t *irq ); * @note It is an error to decrement the counter beyond -(1<<23) * * @param atom pointer to an atomic counter */ -void _mali_osk_atomic_dec( _mali_osk_atomic_t *atom ); +static inline void _mali_osk_atomic_dec( _mali_osk_atomic_t *atom ) +{ + atomic_dec((atomic_t *)&atom->u.val); +} /** @brief Decrement an atomic counter, return new value * * @param atom pointer to an atomic counter * @return The new value, after decrement */ -u32 _mali_osk_atomic_dec_return( _mali_osk_atomic_t *atom ); +static inline u32 _mali_osk_atomic_dec_return( _mali_osk_atomic_t *atom ) +{ + return atomic_dec_return((atomic_t *)&atom->u.val); +} /** @brief Increment an atomic counter * * @note It is an error to increment the counter beyond (1<<23)-1 * * @param atom pointer to an atomic counter */ -void _mali_osk_atomic_inc( _mali_osk_atomic_t *atom ); +static inline void _mali_osk_atomic_inc( _mali_osk_atomic_t *atom ) +{ + atomic_inc((atomic_t *)&atom->u.val); +} /** @brief Increment an atomic counter, return new value * * @param atom pointer to an atomic counter */ -u32 _mali_osk_atomic_inc_return( _mali_osk_atomic_t *atom ); +static inline u32 _mali_osk_atomic_inc_return( _mali_osk_atomic_t *atom ) +{ + return atomic_inc_return((atomic_t *)&atom->u.val); +} /** @brief Initialize an atomic counter * @@ -352,7 +446,12 @@ u32 _mali_osk_atomic_inc_return( _mali_osk_atomic_t *atom ); * @return _MALI_OSK_ERR_OK on success, otherwise, a suitable * _mali_osk_errcode_t on failure. */ -_mali_osk_errcode_t _mali_osk_atomic_init( _mali_osk_atomic_t *atom, u32 val ); +static inline _mali_osk_errcode_t _mali_osk_atomic_init( _mali_osk_atomic_t *atom, u32 val ) +{ + MALI_CHECK_NON_NULL(atom, _MALI_OSK_ERR_INVALID_ARGS); + atomic_set((atomic_t *)&atom->u.val, val); + return _MALI_OSK_ERR_OK; +} /** @brief Read a value from an atomic counter * @@ -362,13 +461,19 @@ _mali_osk_errcode_t _mali_osk_atomic_init( _mali_osk_atomic_t *atom, u32 val ); * * @param atom pointer to an atomic counter */ -u32 _mali_osk_atomic_read( _mali_osk_atomic_t *atom ); +static inline u32 _mali_osk_atomic_read( _mali_osk_atomic_t *atom ) +{ + return atomic_read((atomic_t *)&atom->u.val); +} /** @brief Terminate an atomic counter * * @param atom pointer to an atomic counter */ -void _mali_osk_atomic_term( _mali_osk_atomic_t *atom ); +static inline void _mali_osk_atomic_term( _mali_osk_atomic_t *atom ) +{ + MALI_IGNORE(atom); +} /** @brief Assign a new val to atomic counter, and return the old atomic counter * @@ -376,7 +481,10 @@ void _mali_osk_atomic_term( _mali_osk_atomic_t *atom ); * @param val the new value assign to the atomic counter * @return the old value of the atomic counter */ -u32 _mali_osk_atomic_xchg( _mali_osk_atomic_t *atom, u32 val ); +static inline u32 _mali_osk_atomic_xchg( _mali_osk_atomic_t *atom, u32 val ) +{ + return atomic_xchg((atomic_t*)&atom->u.val, val); +} /** @} */ /* end group _mali_osk_atomic */ diff --git a/drivers/gpu/arm/mali/linux/mali_osk_atomics.c b/drivers/gpu/arm/mali/linux/mali_osk_atomics.c deleted file mode 100644 index 7b34ee2f35f912..00000000000000 --- a/drivers/gpu/arm/mali/linux/mali_osk_atomics.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2010, 2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** - * @file mali_osk_atomics.c - * Implementation of the OS abstraction layer for the kernel device driver - */ - -#include "mali_osk.h" -#include -#include "mali_kernel_common.h" - -void _mali_osk_atomic_dec( _mali_osk_atomic_t *atom ) -{ - atomic_dec((atomic_t *)&atom->u.val); -} - -u32 _mali_osk_atomic_dec_return( _mali_osk_atomic_t *atom ) -{ - return atomic_dec_return((atomic_t *)&atom->u.val); -} - -void _mali_osk_atomic_inc( _mali_osk_atomic_t *atom ) -{ - atomic_inc((atomic_t *)&atom->u.val); -} - -u32 _mali_osk_atomic_inc_return( _mali_osk_atomic_t *atom ) -{ - return atomic_inc_return((atomic_t *)&atom->u.val); -} - -_mali_osk_errcode_t _mali_osk_atomic_init( _mali_osk_atomic_t *atom, u32 val ) -{ - MALI_CHECK_NON_NULL(atom, _MALI_OSK_ERR_INVALID_ARGS); - atomic_set((atomic_t *)&atom->u.val, val); - return _MALI_OSK_ERR_OK; -} - -u32 _mali_osk_atomic_read( _mali_osk_atomic_t *atom ) -{ - return atomic_read((atomic_t *)&atom->u.val); -} - -void _mali_osk_atomic_term( _mali_osk_atomic_t *atom ) -{ - MALI_IGNORE(atom); -} - -u32 _mali_osk_atomic_xchg( _mali_osk_atomic_t *atom, u32 val ) -{ - return atomic_xchg((atomic_t*)&atom->u.val, val); -} From 87610bea2d0a7ce9e0e3ffc55f1ae9f4053c4b4c Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sat, 31 Jan 2015 21:03:05 +0100 Subject: [PATCH 749/788] mali: remove mali_osk_math source --- drivers/gpu/arm/mali/Kbuild | 1 - drivers/gpu/arm/mali/common/mali_osk.h | 10 ++++++-- drivers/gpu/arm/mali/linux/mali_osk_math.c | 27 ---------------------- 3 files changed, 8 insertions(+), 30 deletions(-) delete mode 100644 drivers/gpu/arm/mali/linux/mali_osk_math.c diff --git a/drivers/gpu/arm/mali/Kbuild b/drivers/gpu/arm/mali/Kbuild index 5373d637b17e17..da9bdb38376d6b 100644 --- a/drivers/gpu/arm/mali/Kbuild +++ b/drivers/gpu/arm/mali/Kbuild @@ -47,7 +47,6 @@ mali-y += \ linux/mali_osk_locks.o \ linux/mali_osk_wait_queue.o \ linux/mali_osk_low_level_mem.o \ - linux/mali_osk_math.o \ linux/mali_osk_misc.o \ linux/mali_osk_mali.o \ linux/mali_osk_notification.o \ diff --git a/drivers/gpu/arm/mali/common/mali_osk.h b/drivers/gpu/arm/mali/common/mali_osk.h index e4598589381fa5..45ef378eae9ddc 100644 --- a/drivers/gpu/arm/mali/common/mali_osk.h +++ b/drivers/gpu/arm/mali/common/mali_osk.h @@ -1253,14 +1253,20 @@ u64 _mali_osk_time_get_ns( void ); * @param val 32-bit words to count leading zeros on * @return the number of leading zeros. */ -u32 _mali_osk_clz( u32 val ); +static inline u32 _mali_osk_clz( u32 val ) +{ + return 32-fls(val); +} /** @brief find last (most-significant) bit set * * @param val 32-bit words to count last bit set on * @return last bit set. */ -u32 _mali_osk_fls( u32 val ); +static inline u32 _mali_osk_fls( u32 val ) +{ + return fls(val); +} /** @} */ /* end group _mali_osk_math */ diff --git a/drivers/gpu/arm/mali/linux/mali_osk_math.c b/drivers/gpu/arm/mali/linux/mali_osk_math.c deleted file mode 100644 index c097fcc774218d..00000000000000 --- a/drivers/gpu/arm/mali/linux/mali_osk_math.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2010, 2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** - * @file mali_osk_math.c - * Implementation of the OS abstraction layer for the kernel device driver - */ - -#include "mali_osk.h" -#include - -u32 _mali_osk_clz( u32 input ) -{ - return 32-fls(input); -} - -u32 _mali_osk_fls( u32 input ) -{ - return fls(input); -} From 33d3cf3048e47cb8a452e9c2db3aea50465a0ff8 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sat, 31 Jan 2015 21:09:30 +0100 Subject: [PATCH 750/788] mali: move _mali_osk_break to mali_osk header --- drivers/gpu/arm/mali/common/mali_osk.h | 5 ++++- drivers/gpu/arm/mali/linux/mali_osk_misc.c | 5 ----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/arm/mali/common/mali_osk.h b/drivers/gpu/arm/mali/common/mali_osk.h index 45ef378eae9ddc..7f40488c64df7e 100644 --- a/drivers/gpu/arm/mali/common/mali_osk.h +++ b/drivers/gpu/arm/mali/common/mali_osk.h @@ -1366,7 +1366,10 @@ void _mali_osk_abort(void); * * This function is only used in Debug builds, and is not used in Release builds. */ -void _mali_osk_break(void); +static inline void _mali_osk_break(void) +{ + _mali_osk_abort(); +} /** @brief Return an identificator for calling process. * diff --git a/drivers/gpu/arm/mali/linux/mali_osk_misc.c b/drivers/gpu/arm/mali/linux/mali_osk_misc.c index 28ea90a03ba8a8..cad9966abe33a8 100644 --- a/drivers/gpu/arm/mali/linux/mali_osk_misc.c +++ b/drivers/gpu/arm/mali/linux/mali_osk_misc.c @@ -46,11 +46,6 @@ void _mali_osk_abort(void) *(int *)0 = 0; } -void _mali_osk_break(void) -{ - _mali_osk_abort(); -} - u32 _mali_osk_get_pid(void) { /* Thread group ID is the process ID on Linux */ From 6c835e321f9ce90b8c28a7d56956c94b5ba8d6ef Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sat, 31 Jan 2015 22:30:44 +0100 Subject: [PATCH 751/788] mali: remove ump support --- drivers/gpu/arm/Kconfig | 2 - drivers/gpu/arm/Makefile | 2 +- drivers/gpu/arm/mali/Kbuild | 3 - drivers/gpu/arm/mali/Kconfig | 6 - drivers/gpu/arm/mali/Makefile | 12 - drivers/gpu/arm/mali/common/mali_ukk.h | 13 - .../gpu/arm/mali/linux/mali_kernel_linux.c | 13 - drivers/gpu/arm/mali/linux/mali_memory.c | 5 - drivers/gpu/arm/mali/linux/mali_memory.h | 1 - .../gpu/arm/mali/linux/mali_memory_types.h | 12 - drivers/gpu/arm/mali/linux/mali_memory_ump.c | 215 ------- drivers/gpu/arm/mali/linux/mali_ukk_mem.c | 59 -- .../gpu/arm/mali/linux/mali_ukk_wrappers.h | 5 - drivers/gpu/arm/ump/Kbuild | 88 --- drivers/gpu/arm/ump/Kconfig | 16 - drivers/gpu/arm/ump/Makefile | 67 -- drivers/gpu/arm/ump/Makefile.common | 20 - drivers/gpu/arm/ump/arch-exynos4412/config.h | 30 - drivers/gpu/arm/ump/common/ump_kernel_api.c | 492 -------------- .../gpu/arm/ump/common/ump_kernel_common.c | 370 ----------- .../gpu/arm/ump/common/ump_kernel_common.h | 125 ---- .../common/ump_kernel_descriptor_mapping.c | 155 ----- .../common/ump_kernel_descriptor_mapping.h | 89 --- .../ump/common/ump_kernel_memory_backend.h | 48 -- .../gpu/arm/ump/common/ump_kernel_ref_drv.c | 186 ------ drivers/gpu/arm/ump/common/ump_kernel_types.h | 50 -- drivers/gpu/arm/ump/common/ump_osk.h | 48 -- drivers/gpu/arm/ump/common/ump_uk_types.h | 193 ------ drivers/gpu/arm/ump/common/ump_ukk.h | 60 -- .../linux/license/gpl/ump_kernel_license.h | 30 - drivers/gpu/arm/ump/linux/ump_ioctl.h | 53 -- drivers/gpu/arm/ump/linux/ump_kernel_linux.c | 424 ------------- drivers/gpu/arm/ump/linux/ump_kernel_linux.h | 18 - .../ump_kernel_memory_backend_dedicated.c | 267 -------- .../ump_kernel_memory_backend_dedicated.h | 23 - .../ump/linux/ump_kernel_memory_backend_os.c | 231 ------- .../ump/linux/ump_kernel_memory_backend_os.h | 23 - .../gpu/arm/ump/linux/ump_memory_backend.c | 65 -- drivers/gpu/arm/ump/linux/ump_osk_atomics.c | 27 - .../gpu/arm/ump/linux/ump_osk_low_level_mem.c | 292 --------- drivers/gpu/arm/ump/linux/ump_osk_misc.c | 36 -- .../gpu/arm/ump/linux/ump_ukk_ref_wrappers.c | 71 --- .../gpu/arm/ump/linux/ump_ukk_ref_wrappers.h | 34 - drivers/gpu/arm/ump/linux/ump_ukk_wrappers.c | 280 -------- drivers/gpu/arm/ump/linux/ump_ukk_wrappers.h | 46 -- drivers/gpu/arm/umplock/Kbuild | 13 - drivers/gpu/arm/umplock/Kconfig | 5 - drivers/gpu/arm/umplock/Makefile | 69 -- drivers/gpu/arm/umplock/umplock_driver.c | 598 ------------------ drivers/gpu/arm/umplock/umplock_ioctl.h | 66 -- include/ump/ump_kernel_interface.h | 235 ------- include/ump/ump_kernel_interface_ref_drv.h | 31 - include/ump/ump_kernel_platform.h | 35 - 53 files changed, 1 insertion(+), 5356 deletions(-) delete mode 100644 drivers/gpu/arm/mali/linux/mali_memory_ump.c delete mode 100644 drivers/gpu/arm/ump/Kbuild delete mode 100644 drivers/gpu/arm/ump/Kconfig delete mode 100644 drivers/gpu/arm/ump/Makefile delete mode 100644 drivers/gpu/arm/ump/Makefile.common delete mode 100644 drivers/gpu/arm/ump/arch-exynos4412/config.h delete mode 100644 drivers/gpu/arm/ump/common/ump_kernel_api.c delete mode 100644 drivers/gpu/arm/ump/common/ump_kernel_common.c delete mode 100644 drivers/gpu/arm/ump/common/ump_kernel_common.h delete mode 100644 drivers/gpu/arm/ump/common/ump_kernel_descriptor_mapping.c delete mode 100644 drivers/gpu/arm/ump/common/ump_kernel_descriptor_mapping.h delete mode 100644 drivers/gpu/arm/ump/common/ump_kernel_memory_backend.h delete mode 100644 drivers/gpu/arm/ump/common/ump_kernel_ref_drv.c delete mode 100644 drivers/gpu/arm/ump/common/ump_kernel_types.h delete mode 100644 drivers/gpu/arm/ump/common/ump_osk.h delete mode 100644 drivers/gpu/arm/ump/common/ump_uk_types.h delete mode 100644 drivers/gpu/arm/ump/common/ump_ukk.h delete mode 100644 drivers/gpu/arm/ump/linux/license/gpl/ump_kernel_license.h delete mode 100644 drivers/gpu/arm/ump/linux/ump_ioctl.h delete mode 100644 drivers/gpu/arm/ump/linux/ump_kernel_linux.c delete mode 100644 drivers/gpu/arm/ump/linux/ump_kernel_linux.h delete mode 100644 drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_dedicated.c delete mode 100644 drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_dedicated.h delete mode 100644 drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_os.c delete mode 100644 drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_os.h delete mode 100644 drivers/gpu/arm/ump/linux/ump_memory_backend.c delete mode 100644 drivers/gpu/arm/ump/linux/ump_osk_atomics.c delete mode 100644 drivers/gpu/arm/ump/linux/ump_osk_low_level_mem.c delete mode 100644 drivers/gpu/arm/ump/linux/ump_osk_misc.c delete mode 100644 drivers/gpu/arm/ump/linux/ump_ukk_ref_wrappers.c delete mode 100644 drivers/gpu/arm/ump/linux/ump_ukk_ref_wrappers.h delete mode 100644 drivers/gpu/arm/ump/linux/ump_ukk_wrappers.c delete mode 100644 drivers/gpu/arm/ump/linux/ump_ukk_wrappers.h delete mode 100644 drivers/gpu/arm/umplock/Kbuild delete mode 100644 drivers/gpu/arm/umplock/Kconfig delete mode 100644 drivers/gpu/arm/umplock/Makefile delete mode 100644 drivers/gpu/arm/umplock/umplock_driver.c delete mode 100644 drivers/gpu/arm/umplock/umplock_ioctl.h delete mode 100644 include/ump/ump_kernel_interface.h delete mode 100644 include/ump/ump_kernel_interface_ref_drv.h delete mode 100644 include/ump/ump_kernel_platform.h diff --git a/drivers/gpu/arm/Kconfig b/drivers/gpu/arm/Kconfig index 738741b976e234..25b7223b701afa 100644 --- a/drivers/gpu/arm/Kconfig +++ b/drivers/gpu/arm/Kconfig @@ -1,3 +1 @@ source "drivers/gpu/arm/mali/Kconfig" -source "drivers/gpu/arm/ump/Kconfig" -source "drivers/gpu/arm/umplock/Kconfig" diff --git a/drivers/gpu/arm/Makefile b/drivers/gpu/arm/Makefile index 29e74a91834152..051822abcbf94c 100644 --- a/drivers/gpu/arm/Makefile +++ b/drivers/gpu/arm/Makefile @@ -1 +1 @@ -obj-y += mali/ ump/ umplock/ +obj-y += mali/ diff --git a/drivers/gpu/arm/mali/Kbuild b/drivers/gpu/arm/mali/Kbuild index da9bdb38376d6b..222bbeeb2c5e43 100644 --- a/drivers/gpu/arm/mali/Kbuild +++ b/drivers/gpu/arm/mali/Kbuild @@ -118,8 +118,6 @@ ccflags-$(CONFIG_MALI400_INTERNAL_PROFILING) += -I$(src)/timestamp-$(TIMESTAMP) mali-$(CONFIG_DMA_SHARED_BUFFER) += linux/mali_memory_dma_buf.o mali-$(CONFIG_SYNC) += linux/mali_sync.o -mali-$(CONFIG_MALI400_UMP) += linux/mali_memory_ump.o - mali-$(CONFIG_MALI400_POWER_PERFORMANCE_POLICY) += common/mali_power_performance_policy.o # Tell the Linux build system from which .o file to create the kernel module @@ -196,7 +194,6 @@ VERSION_STRINGS += TARGET_PLATFORM=$(TARGET_PLATFORM) VERSION_STRINGS += MALI_PLATFORM=$(MALI_PLATFORM) VERSION_STRINGS += KDIR=$(KDIR) VERSION_STRINGS += OS_MEMORY_KERNEL_BUFFER_SIZE_IN_MB=$(OS_MEMORY_KERNEL_BUFFER_SIZE_IN_MB) -VERSION_STRINGS += USING_UMP=$(CONFIG_MALI400_UMP) VERSION_STRINGS += USING_PROFILING=$(CONFIG_MALI400_PROFILING) VERSION_STRINGS += USING_INTERNAL_PROFILING=$(CONFIG_MALI400_INTERNAL_PROFILING) VERSION_STRINGS += USING_GPU_UTILIZATION=$(USING_GPU_UTILIZATION) diff --git a/drivers/gpu/arm/mali/Kconfig b/drivers/gpu/arm/mali/Kconfig index 477e6b6a943de9..be69e84d60f328 100644 --- a/drivers/gpu/arm/mali/Kconfig +++ b/drivers/gpu/arm/mali/Kconfig @@ -36,12 +36,6 @@ config MALI400_INTERNAL_PROFILING ---help--- This enables the internal legacy Mali profiling API. -config MALI400_UMP - bool "Enable UMP support" - depends on MALI400 - ---help--- - This enables support for the UMP memory sharing API in the Mali driver. - config MALI400_POWER_PERFORMANCE_POLICY bool "Enable Mali power performance policy" depends on ARM diff --git a/drivers/gpu/arm/mali/Makefile b/drivers/gpu/arm/mali/Makefile index c1b00913436902..9ada7fef8a08d4 100644 --- a/drivers/gpu/arm/mali/Makefile +++ b/drivers/gpu/arm/mali/Makefile @@ -64,18 +64,6 @@ $(error No KDIR found for platform $(TARGET_PLATFORM)) endif -ifeq ($(USING_UMP),1) -export CONFIG_MALI400_UMP=y -export EXTRA_DEFINES += -DCONFIG_MALI400_UMP=1 -ifeq ($(USE_UMPV2),1) -UMP_SYMVERS_FILE ?= ../umpv2/Module.symvers -else -UMP_SYMVERS_FILE ?= ../ump/Module.symvers -endif -KBUILD_EXTRA_SYMBOLS = $(realpath $(UMP_SYMVERS_FILE)) -$(warning $(KBUILD_EXTRA_SYMBOLS)) -endif - # Define host system directory KDIR-$(shell uname -m):=/lib/modules/$(shell uname -r)/build diff --git a/drivers/gpu/arm/mali/common/mali_ukk.h b/drivers/gpu/arm/mali/common/mali_ukk.h index 80ef64dc5e7706..be4e9bc613d4c7 100644 --- a/drivers/gpu/arm/mali/common/mali_ukk.h +++ b/drivers/gpu/arm/mali/common/mali_ukk.h @@ -352,19 +352,6 @@ _mali_osk_errcode_t _mali_ukk_map_external_mem( _mali_uk_map_external_mem_s *arg */ _mali_osk_errcode_t _mali_ukk_unmap_external_mem( _mali_uk_unmap_external_mem_s *args ); -#if defined(CONFIG_MALI400_UMP) -/** @brief Map UMP memory into Mali - * @param args see _mali_uk_attach_ump_mem_s in mali_utgard_uk_types.h - * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. - */ -_mali_osk_errcode_t _mali_ukk_attach_ump_mem( _mali_uk_attach_ump_mem_s *args ); -/** @brief Unmap UMP memory from Mali - * @param args see _mali_uk_release_ump_mem_s in mali_utgard_uk_types.h - * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. - */ -_mali_osk_errcode_t _mali_ukk_release_ump_mem( _mali_uk_release_ump_mem_s *args ); -#endif /* CONFIG_MALI400_UMP */ - /** @brief Determine virtual-to-physical mapping of a contiguous memory range * (optional) * diff --git a/drivers/gpu/arm/mali/linux/mali_kernel_linux.c b/drivers/gpu/arm/mali/linux/mali_kernel_linux.c index 56960ffbb57dee..9bf17de70550c8 100644 --- a/drivers/gpu/arm/mali/linux/mali_kernel_linux.c +++ b/drivers/gpu/arm/mali/linux/mali_kernel_linux.c @@ -661,24 +661,11 @@ static int mali_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, err = mem_dump_mmu_page_table_wrapper(session_data, (_mali_uk_dump_mmu_page_table_s __user *)arg); break; -#if defined(CONFIG_MALI400_UMP) - - case MALI_IOC_MEM_ATTACH_UMP: - err = mem_attach_ump_wrapper(session_data, (_mali_uk_attach_ump_mem_s __user *)arg); - break; - - case MALI_IOC_MEM_RELEASE_UMP: - err = mem_release_ump_wrapper(session_data, (_mali_uk_release_ump_mem_s __user *)arg); - break; - -#else - case MALI_IOC_MEM_ATTACH_UMP: case MALI_IOC_MEM_RELEASE_UMP: /* FALL-THROUGH */ MALI_DEBUG_PRINT(2, ("UMP not supported\n")); err = -ENOTTY; break; -#endif #ifdef CONFIG_DMA_SHARED_BUFFER case MALI_IOC_MEM_ATTACH_DMA_BUF: diff --git a/drivers/gpu/arm/mali/linux/mali_memory.c b/drivers/gpu/arm/mali/linux/mali_memory.c index f9441de07a9840..f2d5e49ff7adba 100644 --- a/drivers/gpu/arm/mali/linux/mali_memory.c +++ b/drivers/gpu/arm/mali/linux/mali_memory.c @@ -43,11 +43,6 @@ static void mali_mem_release(mali_mem_allocation *descriptor) case MALI_MEM_DMA_BUF: #if defined(CONFIG_DMA_SHARED_BUFFER) mali_mem_dma_buf_release(descriptor); -#endif - break; - case MALI_MEM_UMP: -#if defined(CONFIG_MALI400_UMP) - mali_mem_ump_release(descriptor); #endif break; case MALI_MEM_EXTERNAL: diff --git a/drivers/gpu/arm/mali/linux/mali_memory.h b/drivers/gpu/arm/mali/linux/mali_memory.h index a5a02167426d27..0077fc97d5a965 100644 --- a/drivers/gpu/arm/mali/linux/mali_memory.h +++ b/drivers/gpu/arm/mali/linux/mali_memory.h @@ -128,7 +128,6 @@ _mali_osk_errcode_t mali_memory_core_resource_os_memory(u32 size); _mali_osk_errcode_t mali_memory_core_resource_dedicated_memory(u32 start, u32 size); -void mali_mem_ump_release(mali_mem_allocation *descriptor); void mali_mem_external_release(mali_mem_allocation *descriptor); #endif /* __MALI_MEMORY_H__ */ diff --git a/drivers/gpu/arm/mali/linux/mali_memory_types.h b/drivers/gpu/arm/mali/linux/mali_memory_types.h index 8b01f3b584a8ca..44553d3ffe60a5 100644 --- a/drivers/gpu/arm/mali/linux/mali_memory_types.h +++ b/drivers/gpu/arm/mali/linux/mali_memory_types.h @@ -11,17 +11,12 @@ #ifndef __MALI_MEMORY_TYPES_H__ #define __MALI_MEMORY_TYPES_H__ -#if defined(CONFIG_MALI400_UMP) -#include -#endif - typedef u32 mali_address_t; typedef enum mali_mem_type { MALI_MEM_OS, MALI_MEM_EXTERNAL, MALI_MEM_DMA_BUF, - MALI_MEM_UMP, MALI_MEM_BLOCK, } mali_mem_type; @@ -41,12 +36,6 @@ typedef struct mali_mem_external { u32 size; } mali_mem_external; -typedef struct mali_mem_ump { -#if defined(CONFIG_MALI400_UMP) - ump_dd_handle handle; -#endif -} mali_mem_ump; - typedef struct block_allocator_allocation { /* The list will be released in reverse order */ struct block_info *last_allocated; @@ -86,7 +75,6 @@ typedef struct mali_mem_allocation { mali_mem_os_mem os_mem; /**< MALI_MEM_OS */ mali_mem_external ext_mem; /**< MALI_MEM_EXTERNAL */ mali_mem_dma_buf dma_buf; /**< MALI_MEM_DMA_BUF */ - mali_mem_ump ump_mem; /**< MALI_MEM_UMP */ mali_mem_block_mem block_mem; /**< MALI_MEM_BLOCK */ }; diff --git a/drivers/gpu/arm/mali/linux/mali_memory_ump.c b/drivers/gpu/arm/mali/linux/mali_memory_ump.c deleted file mode 100644 index c5dca465ee3539..00000000000000 --- a/drivers/gpu/arm/mali/linux/mali_memory_ump.c +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright (C) 2012-2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "mali_ukk.h" -#include "mali_osk.h" -#include "mali_kernel_common.h" -#include "mali_session.h" -#include "mali_kernel_linux.h" - -#include "mali_memory.h" - -#include - -static int mali_ump_map(struct mali_session_data *session, mali_mem_allocation *descriptor) -{ - ump_dd_handle ump_mem; - u32 nr_blocks; - u32 i; - ump_dd_physical_block *ump_blocks; - struct mali_page_directory *pagedir; - u32 offset = 0; - u32 prop; - _mali_osk_errcode_t err; - - MALI_DEBUG_ASSERT_POINTER(session); - MALI_DEBUG_ASSERT_POINTER(descriptor); - MALI_DEBUG_ASSERT(MALI_MEM_UMP == descriptor->type); - - ump_mem = descriptor->ump_mem.handle; - MALI_DEBUG_ASSERT(UMP_DD_HANDLE_INVALID != ump_mem); - - nr_blocks = ump_dd_phys_block_count_get(ump_mem); - if (nr_blocks == 0) { - MALI_DEBUG_PRINT(1, ("No block count\n")); - return -EINVAL; - } - - ump_blocks = _mali_osk_malloc(sizeof(*ump_blocks)*nr_blocks); - if (NULL == ump_blocks) { - return -ENOMEM; - } - - if (UMP_DD_INVALID == ump_dd_phys_blocks_get(ump_mem, ump_blocks, nr_blocks)) { - _mali_osk_free(ump_blocks); - return -EFAULT; - } - - pagedir = session->page_directory; - prop = descriptor->mali_mapping.properties; - - err = mali_mem_mali_map_prepare(descriptor); - if (_MALI_OSK_ERR_OK != err) { - MALI_DEBUG_PRINT(1, ("Mapping of UMP memory failed\n")); - - _mali_osk_free(ump_blocks); - return -ENOMEM; - } - - for(i = 0; i < nr_blocks; ++i) { - u32 virt = descriptor->mali_mapping.addr + offset; - - MALI_DEBUG_PRINT(7, ("Mapping in 0x%08x size %d\n", ump_blocks[i].addr , ump_blocks[i].size)); - - mali_mmu_pagedir_update(pagedir, virt, ump_blocks[i].addr, - ump_blocks[i].size, prop); - - offset += ump_blocks[i].size; - } - - if (descriptor->flags & _MALI_MAP_EXTERNAL_MAP_GUARD_PAGE) { - u32 virt = descriptor->mali_mapping.addr + offset; - - /* Map in an extra virtual guard page at the end of the VMA */ - MALI_DEBUG_PRINT(6, ("Mapping in extra guard page\n")); - - mali_mmu_pagedir_update(pagedir, virt, ump_blocks[0].addr, _MALI_OSK_MALI_PAGE_SIZE, prop); - - offset += _MALI_OSK_MALI_PAGE_SIZE; - } - - _mali_osk_free(ump_blocks); - - return 0; -} - -void mali_ump_unmap(struct mali_session_data *session, mali_mem_allocation *descriptor) -{ - ump_dd_handle ump_mem; - struct mali_page_directory *pagedir; - - ump_mem = descriptor->ump_mem.handle; - pagedir = session->page_directory; - - MALI_DEBUG_ASSERT(UMP_DD_HANDLE_INVALID != ump_mem); - - mali_mem_mali_map_free(descriptor); - - ump_dd_reference_release(ump_mem); - return; -} - -_mali_osk_errcode_t _mali_ukk_attach_ump_mem(_mali_uk_attach_ump_mem_s *args) -{ - ump_dd_handle ump_mem; - struct mali_session_data *session; - mali_mem_allocation *descriptor; - int md, ret; - - MALI_DEBUG_ASSERT_POINTER(args); - MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); - - session = (struct mali_session_data *)args->ctx; - MALI_CHECK_NON_NULL(session, _MALI_OSK_ERR_INVALID_ARGS); - - /* check arguments */ - /* NULL might be a valid Mali address */ - if (!args->size) MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); - - /* size must be a multiple of the system page size */ - if (args->size % _MALI_OSK_MALI_PAGE_SIZE) MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); - - MALI_DEBUG_PRINT(3, - ("Requested to map ump memory with secure id %d into virtual memory 0x%08X, size 0x%08X\n", - args->secure_id, args->mali_address, args->size)); - - ump_mem = ump_dd_handle_create_from_secure_id((int)args->secure_id); - - if (UMP_DD_HANDLE_INVALID == ump_mem) MALI_ERROR(_MALI_OSK_ERR_FAULT); - - descriptor = mali_mem_descriptor_create(session, MALI_MEM_UMP); - if (NULL == descriptor) { - ump_dd_reference_release(ump_mem); - MALI_ERROR(_MALI_OSK_ERR_NOMEM); - } - - descriptor->ump_mem.handle = ump_mem; - descriptor->mali_mapping.addr = args->mali_address; - descriptor->size = args->size; - descriptor->mali_mapping.properties = MALI_MMU_FLAGS_DEFAULT; - descriptor->flags |= MALI_MEM_FLAG_DONT_CPU_MAP; - - if (args->flags & _MALI_MAP_EXTERNAL_MAP_GUARD_PAGE) { - descriptor->flags = MALI_MEM_FLAG_MALI_GUARD_PAGE; - } - - _mali_osk_mutex_wait(session->memory_lock); - - ret = mali_ump_map(session, descriptor); - if (0 != ret) { - _mali_osk_mutex_signal(session->memory_lock); - ump_dd_reference_release(ump_mem); - mali_mem_descriptor_destroy(descriptor); - MALI_ERROR(_MALI_OSK_ERR_NOMEM); - } - - _mali_osk_mutex_signal(session->memory_lock); - - - if (_MALI_OSK_ERR_OK != mali_descriptor_mapping_allocate_mapping(session->descriptor_mapping, descriptor, &md)) { - ump_dd_reference_release(ump_mem); - mali_mem_descriptor_destroy(descriptor); - MALI_ERROR(_MALI_OSK_ERR_FAULT); - } - - args->cookie = md; - - MALI_DEBUG_PRINT(5,("Returning from UMP attach\n")); - - MALI_SUCCESS; -} - -void mali_mem_ump_release(mali_mem_allocation *descriptor) -{ - struct mali_session_data *session = descriptor->session; - - MALI_DEBUG_ASSERT(MALI_MEM_UMP == descriptor->type); - - mali_ump_unmap(session, descriptor); -} - -_mali_osk_errcode_t _mali_ukk_release_ump_mem(_mali_uk_release_ump_mem_s *args) -{ - mali_mem_allocation * descriptor; - struct mali_session_data *session; - - MALI_DEBUG_ASSERT_POINTER(args); - MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); - - session = (struct mali_session_data *)args->ctx; - MALI_CHECK_NON_NULL(session, _MALI_OSK_ERR_INVALID_ARGS); - - if (_MALI_OSK_ERR_OK != mali_descriptor_mapping_get(session->descriptor_mapping, args->cookie, (void**)&descriptor)) { - MALI_DEBUG_PRINT(1, ("Invalid memory descriptor %d used to release ump memory\n", args->cookie)); - MALI_ERROR(_MALI_OSK_ERR_FAULT); - } - - descriptor = mali_descriptor_mapping_free(session->descriptor_mapping, args->cookie); - - if (NULL != descriptor) { - _mali_osk_mutex_wait(session->memory_lock); - mali_mem_ump_release(descriptor); - _mali_osk_mutex_signal(session->memory_lock); - - mali_mem_descriptor_destroy(descriptor); - } - - MALI_SUCCESS; -} diff --git a/drivers/gpu/arm/mali/linux/mali_ukk_mem.c b/drivers/gpu/arm/mali/linux/mali_ukk_mem.c index 083c909ab09ef1..b50d129f99b850 100644 --- a/drivers/gpu/arm/mali/linux/mali_ukk_mem.c +++ b/drivers/gpu/arm/mali/linux/mali_ukk_mem.c @@ -111,65 +111,6 @@ int mem_unmap_ext_wrapper(struct mali_session_data *session_data, _mali_uk_unmap return map_errcode(err_code); } -#if defined(CONFIG_MALI400_UMP) -int mem_release_ump_wrapper(struct mali_session_data *session_data, _mali_uk_release_ump_mem_s __user * argument) -{ - _mali_uk_release_ump_mem_s uk_args; - _mali_osk_errcode_t err_code; - - /* validate input */ - /* the session_data pointer was validated by caller */ - MALI_CHECK_NON_NULL( argument, -EINVAL); - - /* get call arguments from user space. copy_from_user returns how many bytes which where NOT copied */ - if ( 0 != copy_from_user(&uk_args, (void __user *)argument, sizeof(_mali_uk_release_ump_mem_s)) ) { - return -EFAULT; - } - - uk_args.ctx = session_data; - err_code = _mali_ukk_release_ump_mem( &uk_args ); - - /* Return the error that _mali_ukk_free_big_block produced */ - return map_errcode(err_code); -} - -int mem_attach_ump_wrapper(struct mali_session_data *session_data, _mali_uk_attach_ump_mem_s __user * argument) -{ - _mali_uk_attach_ump_mem_s uk_args; - _mali_osk_errcode_t err_code; - - /* validate input */ - /* the session_data pointer was validated by caller */ - MALI_CHECK_NON_NULL( argument, -EINVAL); - - /* get call arguments from user space. copy_from_user returns how many bytes which where NOT copied */ - if ( 0 != copy_from_user(&uk_args, (void __user *)argument, sizeof(_mali_uk_attach_ump_mem_s)) ) { - return -EFAULT; - } - - uk_args.ctx = session_data; - err_code = _mali_ukk_attach_ump_mem( &uk_args ); - - if (0 != put_user(uk_args.cookie, &argument->cookie)) { - if (_MALI_OSK_ERR_OK == err_code) { - /* Rollback */ - _mali_uk_release_ump_mem_s uk_args_unmap; - - uk_args_unmap.ctx = session_data; - uk_args_unmap.cookie = uk_args.cookie; - err_code = _mali_ukk_release_ump_mem( &uk_args_unmap ); - if (_MALI_OSK_ERR_OK != err_code) { - MALI_DEBUG_PRINT(4, ("reverting _mali_ukk_attach_mem, as a result of failing put_user(), failed\n")); - } - } - return -EFAULT; - } - - /* Return the error that _mali_ukk_map_external_ump_mem produced */ - return map_errcode(err_code); -} -#endif /* CONFIG_MALI400_UMP */ - int mem_query_mmu_page_table_dump_size_wrapper(struct mali_session_data *session_data, _mali_uk_query_mmu_page_table_dump_size_s __user * uargs) { _mali_uk_query_mmu_page_table_dump_size_s kargs; diff --git a/drivers/gpu/arm/mali/linux/mali_ukk_wrappers.h b/drivers/gpu/arm/mali/linux/mali_ukk_wrappers.h index 0883b6c0d7b7cc..e0db4378545870 100644 --- a/drivers/gpu/arm/mali/linux/mali_ukk_wrappers.h +++ b/drivers/gpu/arm/mali/linux/mali_ukk_wrappers.h @@ -41,11 +41,6 @@ int timeline_create_sync_fence_wrapper(struct mali_session_data *session, _mali_ int soft_job_start_wrapper(struct mali_session_data *session, _mali_uk_soft_job_start_s __user *uargs); int soft_job_signal_wrapper(struct mali_session_data *session, _mali_uk_soft_job_signal_s __user *uargs); -#if defined(CONFIG_MALI400_UMP) -int mem_attach_ump_wrapper(struct mali_session_data *session_data, _mali_uk_attach_ump_mem_s __user * argument); -int mem_release_ump_wrapper(struct mali_session_data *session_data, _mali_uk_release_ump_mem_s __user * argument); -#endif - int pp_start_job_wrapper(struct mali_session_data *session_data, _mali_uk_pp_start_job_s __user *uargs); int pp_and_gp_start_job_wrapper(struct mali_session_data *session_data, _mali_uk_pp_and_gp_start_job_s __user *uargs); int pp_get_number_of_cores_wrapper(struct mali_session_data *session_data, _mali_uk_get_pp_number_of_cores_s __user *uargs); diff --git a/drivers/gpu/arm/ump/Kbuild b/drivers/gpu/arm/ump/Kbuild deleted file mode 100644 index 54d91b149c4a0d..00000000000000 --- a/drivers/gpu/arm/ump/Kbuild +++ /dev/null @@ -1,88 +0,0 @@ -# -# Copyright (C) 2010-2012 ARM Limited. All rights reserved. -# -# This program is free software and is provided to you under the terms of the GNU General Public License version 2 -# as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. -# -# A copy of the licence is included with the program, and can also be obtained from Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# - -# Set default configuration to use, if Makefile didn't provide one. -# Change this to use a different config.h -CONFIG ?= exynos4412 - -# Validate selected config -ifneq ($(shell [ -d $(src)/arch-$(CONFIG) ] && [ -f $(src)/arch-$(CONFIG)/config.h ] && echo "OK"), OK) -$(warning Current directory is $(src)) -$(error No configuration found for config $(CONFIG). Check that arch-$(CONFIG)/config.h exists) -else -# Link arch to the selected arch-config directory -$(shell [ -L $(src)/arch ] && rm $(src)/arch) -$(shell ln -sf arch-$(CONFIG) $(src)/arch) -$(shell touch $(src)/arch/config.h) -endif - -UDD_FILE_PREFIX = ../mali/ - -# Get subversion revision number, fall back to 0000 if no svn info is available -SVN_INFO = (cd $(src); svn info 2>/dev/null) - -ifneq ($(shell $(SVN_INFO) 2>/dev/null),) -# SVN detected -SVN_REV := $(shell $(SVN_INFO) | grep '^Revision: '| sed -e 's/^Revision: //' 2>/dev/null) -DRIVER_REV := $(MALI_RELEASE_NAME)-r$(SVN_REV) -CHANGE_DATE := $(shell $(SVN_INFO) | grep '^Last Changed Date: ' | cut -d: -f2- | cut -b2-) -CHANGED_REVISION := $(shell $(SVN_INFO) | grep '^Last Changed Rev: ' | cut -d: -f2- | cut -b2-) -REPO_URL := $(shell $(SVN_INFO) | grep '^URL: ' | cut -d: -f2- | cut -b2-) - -else # SVN -GIT_REV := $(shell cd $(src); git describe --always 2>/dev/null) -ifneq ($(GIT_REV),) -# Git detected -DRIVER_REV := $(MALI_RELEASE_NAME)-$(GIT_REV) -CHANGE_DATE := $(shell cd $(src); git log -1 --format="%ci") -CHANGED_REVISION := $(GIT_REV) -REPO_URL := $(shell cd $(src); git describe --all --always 2>/dev/null) - -else # Git -# No Git or SVN detected -DRIVER_REV := $(MALI_RELEASE_NAME) -CHANGE_DATE := $(MALI_RELEASE_NAME) -CHANGED_REVISION := $(MALI_RELEASE_NAME) -endif -endif - -ccflags-y += -DSVN_REV=$(SVN_REV) -ccflags-y += -DSVN_REV_STRING=\"$(DRIVER_REV)\" - -ccflags-y += -I$(src) -I$(src)/common -I$(src)/linux -I$(src)/../mali/common -I$(src)/../mali/linux -ccflags-y += -DMALI_STATE_TRACKING=0 -ccflags-y += -DMALI_ENABLE_CPU_CYCLES=0 -ccflags-$(CONFIG_UMP_DEBUG) += -DDEBUG - -# For customer releases the Linux Device Drivers will be provided as ARM proprietary and GPL releases: -# The ARM proprietary product will only include the license/proprietary directory -# The GPL product will only include the license/gpl directory - -ifeq ($(wildcard $(src)/linux/license/gpl/*),) -ccflags-y += -I$(src)/linux/license/proprietary -I$(src)/../mali/linux/license/proprietary -else -ccflags-y += -I$(src)/linux/license/gpl -I$(src)/../mali/linux/license/gpl -endif - -ump-y = common/ump_kernel_common.o \ - common/ump_kernel_descriptor_mapping.o \ - common/ump_kernel_api.o \ - common/ump_kernel_ref_drv.o \ - linux/ump_kernel_linux.o \ - linux/ump_kernel_memory_backend_os.o \ - linux/ump_kernel_memory_backend_dedicated.o \ - linux/ump_memory_backend.o \ - linux/ump_ukk_wrappers.o \ - linux/ump_ukk_ref_wrappers.o \ - linux/ump_osk_atomics.o \ - linux/ump_osk_low_level_mem.o \ - linux/ump_osk_misc.o - -obj-$(CONFIG_UMP) := ump.o diff --git a/drivers/gpu/arm/ump/Kconfig b/drivers/gpu/arm/ump/Kconfig deleted file mode 100644 index 3ae316c90ca3e8..00000000000000 --- a/drivers/gpu/arm/ump/Kconfig +++ /dev/null @@ -1,16 +0,0 @@ -config UMP - tristate "UMP support" - depends on ARM - ---help--- - This enables support for the UMP memory allocation and sharing API. - - To compile this driver as a module, choose M here: the module will be - called ump. - -config UMP_DEBUG - bool "Enable extra debug in UMP" - depends on UMP - default y - ---help--- - This enabled extra debug checks and messages in UMP. - diff --git a/drivers/gpu/arm/ump/Makefile b/drivers/gpu/arm/ump/Makefile deleted file mode 100644 index e2aa8e5cd1c551..00000000000000 --- a/drivers/gpu/arm/ump/Makefile +++ /dev/null @@ -1,67 +0,0 @@ -# -# Copyright (C) 2010-2012 ARM Limited. All rights reserved. -# -# This program is free software and is provided to you under the terms of the GNU General Public License version 2 -# as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. -# -# A copy of the licence is included with the program, and can also be obtained from Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# - -# For each arch check: CROSS_COMPILE , KDIR , CFLAGS += -DARCH - -export ARCH ?= arm -BUILD ?= debug - -check_cc2 = \ - $(shell if $(1) -S -o /dev/null -xc /dev/null > /dev/null 2>&1; \ - then \ - echo "$(2)"; \ - else \ - echo "$(3)"; \ - fi ;) - -# Check that required parameters are supplied. -ifeq ($(CONFIG),) -$(error "CONFIG must be specified.") -endif -ifeq ($(CPU)$(KDIR),) -$(error "KDIR or CPU must be specified.") -endif - -# Get any user defined KDIR- or maybe even a hardcoded KDIR --include KDIR_CONFIGURATION - -# Define host system directory -KDIR-$(shell uname -m):=/lib/modules/$(shell uname -r)/build - -ifeq ($(ARCH), arm) -# when compiling for ARM we're cross compiling -export CROSS_COMPILE ?= $(call check_cc2, arm-linux-gnueabi-gcc, arm-linux-gnueabi-, arm-none-linux-gnueabi-) -endif - -# look up KDIR based om CPU selection -KDIR ?= $(KDIR-$(CPU)) - -export CONFIG - -export CONFIG_UMP := m -ifeq ($(BUILD),debug) -export CONFIG_UMP_DEBUG := y -else -export CONFIG_UMP_DEBUG := n -endif - -ifeq ($(KDIR),) -$(error No KDIR found for platform $(CPU)) -endif - -all: - $(MAKE) -C $(KDIR) M=$(CURDIR) modules - -kernelrelease: - $(MAKE) -C $(KDIR) kernelrelease - -clean: - $(MAKE) -C $(KDIR) M=$(CURDIR) clean - $(MAKE) -C $(KDIR) M=$(CURDIR)/../mali clean diff --git a/drivers/gpu/arm/ump/Makefile.common b/drivers/gpu/arm/ump/Makefile.common deleted file mode 100644 index e750ed7d5e3aaf..00000000000000 --- a/drivers/gpu/arm/ump/Makefile.common +++ /dev/null @@ -1,20 +0,0 @@ -# -# Copyright (C) 2010-2011, 2013 ARM Limited. All rights reserved. -# -# This program is free software and is provided to you under the terms of the GNU General Public License version 2 -# as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. -# -# A copy of the licence is included with the program, and can also be obtained from Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# - -SRC = $(UMP_FILE_PREFIX)common/ump_kernel_common.c \ - $(UMP_FILE_PREFIX)common/ump_kernel_descriptor_mapping.c \ - $(UMP_FILE_PREFIX)common/ump_kernel_api.c \ - $(UMP_FILE_PREFIX)common/ump_kernel_ref_drv.c - -# Get subversion revision number, fall back to 0000 if no svn info is available -SVN_REV:=$(shell ((svnversion | grep -qv exported && echo -n 'Revision: ' && svnversion) || git svn info | sed -e 's/$$$$/M/' | grep '^Revision: ' || echo ${MALI_RELEASE_NAME}) 2>/dev/null | sed -e 's/^Revision: //') - -EXTRA_CFLAGS += -DSVN_REV=$(SVN_REV) -EXTRA_CFLAGS += -DSVN_REV_STRING=\"$(SVN_REV)\" diff --git a/drivers/gpu/arm/ump/arch-exynos4412/config.h b/drivers/gpu/arm/ump/arch-exynos4412/config.h deleted file mode 100644 index c2608822f3ca35..00000000000000 --- a/drivers/gpu/arm/ump/arch-exynos4412/config.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2010-2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef __ARCH_CONFIG_H__ -#define __ARCH_CONFIG_H__ - -/* -ARCH_UMP_BACKEND_DEFAULT - 0 specifies the dedicated memory allocator. - 1 specifies the OS memory allocator. -ARCH_UMP_MEMORY_ADDRESS_DEFAULT - This is only required for the dedicated memory allocator, and specifies - the physical start address of the memory block reserved for UMP. -ARCH_UMP_MEMORY_SIZE_DEFAULT - This specified the size of the memory block reserved for UMP, or the - maximum limit for allocations from the OS. -*/ - -#define ARCH_UMP_BACKEND_DEFAULT 1 -#define ARCH_UMP_MEMORY_ADDRESS_DEFAULT 0xE1000000 -#define ARCH_UMP_MEMORY_SIZE_DEFAULT 16UL * 1024UL * 1024UL - -#endif /* __ARCH_CONFIG_H__ */ diff --git a/drivers/gpu/arm/ump/common/ump_kernel_api.c b/drivers/gpu/arm/ump/common/ump_kernel_api.c deleted file mode 100644 index 58df0a2d31e4a9..00000000000000 --- a/drivers/gpu/arm/ump/common/ump_kernel_api.c +++ /dev/null @@ -1,492 +0,0 @@ -/* - * Copyright (C) 2010-2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "mali_osk.h" -#include "mali_osk_list.h" -#include "ump_osk.h" -#include "ump_uk_types.h" -#include -#include "ump_kernel_common.h" - - - -/* ---------------- UMP kernel space API functions follows ---------------- */ - - - -UMP_KERNEL_API_EXPORT ump_secure_id ump_dd_secure_id_get(ump_dd_handle memh) -{ - ump_dd_mem * mem = (ump_dd_mem *)memh; - - DEBUG_ASSERT_POINTER(mem); - - DBG_MSG(5, ("Returning secure ID. ID: %u\n", mem->secure_id)); - - return mem->secure_id; -} - - - -UMP_KERNEL_API_EXPORT ump_dd_handle ump_dd_handle_create_from_secure_id(ump_secure_id secure_id) -{ - ump_dd_mem * mem; - - _mali_osk_mutex_wait(device.secure_id_map_lock); - - DBG_MSG(5, ("Getting handle from secure ID. ID: %u\n", secure_id)); - if (0 != ump_descriptor_mapping_get(device.secure_id_map, (int)secure_id, (void**)&mem)) { - _mali_osk_mutex_signal(device.secure_id_map_lock); - DBG_MSG(1, ("Secure ID not found. ID: %u\n", secure_id)); - return UMP_DD_HANDLE_INVALID; - } - - ump_dd_reference_add(mem); - - _mali_osk_mutex_signal(device.secure_id_map_lock); - - return (ump_dd_handle)mem; -} - - - -UMP_KERNEL_API_EXPORT unsigned long ump_dd_phys_block_count_get(ump_dd_handle memh) -{ - ump_dd_mem * mem = (ump_dd_mem*) memh; - - DEBUG_ASSERT_POINTER(mem); - - return mem->nr_blocks; -} - - - -UMP_KERNEL_API_EXPORT ump_dd_status_code ump_dd_phys_blocks_get(ump_dd_handle memh, ump_dd_physical_block * blocks, unsigned long num_blocks) -{ - ump_dd_mem * mem = (ump_dd_mem *)memh; - - DEBUG_ASSERT_POINTER(mem); - - if (blocks == NULL) { - DBG_MSG(1, ("NULL parameter in ump_dd_phys_blocks_get()\n")); - return UMP_DD_INVALID; - } - - if (mem->nr_blocks != num_blocks) { - DBG_MSG(1, ("Specified number of blocks do not match actual number of blocks\n")); - return UMP_DD_INVALID; - } - - DBG_MSG(5, ("Returning physical block information. ID: %u\n", mem->secure_id)); - - _mali_osk_memcpy(blocks, mem->block_array, sizeof(ump_dd_physical_block) * mem->nr_blocks); - - return UMP_DD_SUCCESS; -} - - - -UMP_KERNEL_API_EXPORT ump_dd_status_code ump_dd_phys_block_get(ump_dd_handle memh, unsigned long index, ump_dd_physical_block * block) -{ - ump_dd_mem * mem = (ump_dd_mem *)memh; - - DEBUG_ASSERT_POINTER(mem); - - if (block == NULL) { - DBG_MSG(1, ("NULL parameter in ump_dd_phys_block_get()\n")); - return UMP_DD_INVALID; - } - - if (index >= mem->nr_blocks) { - DBG_MSG(5, ("Invalid index specified in ump_dd_phys_block_get()\n")); - return UMP_DD_INVALID; - } - - DBG_MSG(5, ("Returning physical block information. ID: %u, index: %lu\n", mem->secure_id, index)); - - *block = mem->block_array[index]; - - return UMP_DD_SUCCESS; -} - - - -UMP_KERNEL_API_EXPORT unsigned long ump_dd_size_get(ump_dd_handle memh) -{ - ump_dd_mem * mem = (ump_dd_mem*)memh; - - DEBUG_ASSERT_POINTER(mem); - - DBG_MSG(5, ("Returning size. ID: %u, size: %lu\n", mem->secure_id, mem->size_bytes)); - - return mem->size_bytes; -} - - - -UMP_KERNEL_API_EXPORT void ump_dd_reference_add(ump_dd_handle memh) -{ - ump_dd_mem * mem = (ump_dd_mem*)memh; - int new_ref; - - DEBUG_ASSERT_POINTER(mem); - - new_ref = _ump_osk_atomic_inc_and_read(&mem->ref_count); - - DBG_MSG(5, ("Memory reference incremented. ID: %u, new value: %d\n", mem->secure_id, new_ref)); -} - - - -UMP_KERNEL_API_EXPORT void ump_dd_reference_release(ump_dd_handle memh) -{ - int new_ref; - ump_dd_mem * mem = (ump_dd_mem*)memh; - - DEBUG_ASSERT_POINTER(mem); - - /* We must hold this mutex while doing the atomic_dec_and_read, to protect - that elements in the ump_descriptor_mapping table is always valid. If they - are not, userspace may accidently map in this secure_ids right before its freed - giving a mapped backdoor into unallocated memory.*/ - _mali_osk_mutex_wait(device.secure_id_map_lock); - - new_ref = _ump_osk_atomic_dec_and_read(&mem->ref_count); - - DBG_MSG(5, ("Memory reference decremented. ID: %u, new value: %d\n", mem->secure_id, new_ref)); - - if (0 == new_ref) { - DBG_MSG(3, ("Final release of memory. ID: %u\n", mem->secure_id)); - - ump_descriptor_mapping_free(device.secure_id_map, (int)mem->secure_id); - - _mali_osk_mutex_signal(device.secure_id_map_lock); - mem->release_func(mem->ctx, mem); - _mali_osk_free(mem); - } else { - _mali_osk_mutex_signal(device.secure_id_map_lock); - } -} - - - -/* --------------- Handling of user space requests follows --------------- */ - - -_mali_osk_errcode_t _ump_uku_get_api_version( _ump_uk_api_version_s *args ) -{ - ump_session_data * session_data; - - DEBUG_ASSERT_POINTER( args ); - DEBUG_ASSERT_POINTER( args->ctx ); - - session_data = (ump_session_data *)args->ctx; - - /* check compatability */ - if (args->version == UMP_IOCTL_API_VERSION) { - DBG_MSG(3, ("API version set to newest %d (compatible)\n", GET_VERSION(args->version))); - args->compatible = 1; - session_data->api_version = args->version; - } else if (args->version == MAKE_VERSION_ID(1)) { - DBG_MSG(2, ("API version set to depricated: %d (compatible)\n", GET_VERSION(args->version))); - args->compatible = 1; - session_data->api_version = args->version; - } else { - DBG_MSG(2, ("API version set to %d (incompatible with client version %d)\n", GET_VERSION(UMP_IOCTL_API_VERSION), GET_VERSION(args->version))); - args->compatible = 0; - args->version = UMP_IOCTL_API_VERSION; /* report our version */ - } - - return _MALI_OSK_ERR_OK; -} - - -_mali_osk_errcode_t _ump_ukk_release( _ump_uk_release_s *release_info ) -{ - ump_session_memory_list_element * session_memory_element; - ump_session_memory_list_element * tmp; - ump_session_data * session_data; - _mali_osk_errcode_t ret = _MALI_OSK_ERR_INVALID_FUNC; - int secure_id; - - DEBUG_ASSERT_POINTER( release_info ); - DEBUG_ASSERT_POINTER( release_info->ctx ); - - /* Retreive the session data */ - session_data = (ump_session_data*)release_info->ctx; - - /* If there are many items in the memory session list we - * could be de-referencing this pointer a lot so keep a local copy - */ - secure_id = release_info->secure_id; - - DBG_MSG(4, ("Releasing memory with IOCTL, ID: %u\n", secure_id)); - - /* Iterate through the memory list looking for the requested secure ID */ - _mali_osk_mutex_wait(session_data->lock); - _MALI_OSK_LIST_FOREACHENTRY(session_memory_element, tmp, &session_data->list_head_session_memory_list, ump_session_memory_list_element, list) { - if ( session_memory_element->mem->secure_id == secure_id) { - ump_dd_mem *release_mem; - - release_mem = session_memory_element->mem; - _mali_osk_list_del(&session_memory_element->list); - ump_dd_reference_release(release_mem); - _mali_osk_free(session_memory_element); - - ret = _MALI_OSK_ERR_OK; - break; - } - } - - _mali_osk_mutex_signal(session_data->lock); - DBG_MSG_IF(1, _MALI_OSK_ERR_OK != ret, ("UMP memory with ID %u does not belong to this session.\n", secure_id)); - - DBG_MSG(4, ("_ump_ukk_release() returning 0x%x\n", ret)); - return ret; -} - -_mali_osk_errcode_t _ump_ukk_size_get( _ump_uk_size_get_s *user_interaction ) -{ - ump_dd_mem * mem; - _mali_osk_errcode_t ret = _MALI_OSK_ERR_FAULT; - - DEBUG_ASSERT_POINTER( user_interaction ); - - /* We lock the mappings so things don't get removed while we are looking for the memory */ - _mali_osk_mutex_wait(device.secure_id_map_lock); - if (0 == ump_descriptor_mapping_get(device.secure_id_map, (int)user_interaction->secure_id, (void**)&mem)) { - user_interaction->size = mem->size_bytes; - DBG_MSG(4, ("Returning size. ID: %u, size: %lu ", (ump_secure_id)user_interaction->secure_id, (unsigned long)user_interaction->size)); - ret = _MALI_OSK_ERR_OK; - } else { - user_interaction->size = 0; - DBG_MSG(1, ("Failed to look up mapping in ump_ioctl_size_get(). ID: %u\n", (ump_secure_id)user_interaction->secure_id)); - } - - _mali_osk_mutex_signal(device.secure_id_map_lock); - return ret; -} - - - -void _ump_ukk_msync( _ump_uk_msync_s *args ) -{ - ump_dd_mem * mem = NULL; - void *virtual = NULL; - u32 size = 0; - u32 offset = 0; - - _mali_osk_mutex_wait(device.secure_id_map_lock); - ump_descriptor_mapping_get(device.secure_id_map, (int)args->secure_id, (void**)&mem); - - if (NULL == mem) { - _mali_osk_mutex_signal(device.secure_id_map_lock); - DBG_MSG(1, ("Failed to look up mapping in _ump_ukk_msync(). ID: %u\n", (ump_secure_id)args->secure_id)); - return; - } - /* Ensure the memory doesn't dissapear when we are flushing it. */ - ump_dd_reference_add(mem); - _mali_osk_mutex_signal(device.secure_id_map_lock); - - /* Returns the cache settings back to Userspace */ - args->is_cached=mem->is_cached; - - /* If this flag is the only one set, we should not do the actual flush, only the readout */ - if ( _UMP_UK_MSYNC_READOUT_CACHE_ENABLED==args->op ) { - DBG_MSG(3, ("_ump_ukk_msync READOUT ID: %u Enabled: %d\n", (ump_secure_id)args->secure_id, mem->is_cached)); - goto msync_release_and_return; - } - - /* Nothing to do if the memory is not caches */ - if ( 0==mem->is_cached ) { - DBG_MSG(3, ("_ump_ukk_msync IGNORING ID: %u Enabled: %d OP: %d\n", (ump_secure_id)args->secure_id, mem->is_cached, args->op)); - goto msync_release_and_return; - } - DBG_MSG(3, ("UMP[%02u] _ump_ukk_msync Flush OP: %d Address: 0x%08x Mapping: 0x%08x\n", - (ump_secure_id)args->secure_id, args->op, args->address, args->mapping)); - - if ( args->address ) { - virtual = (void *)((u32)args->address); - offset = (u32)((args->address) - (args->mapping)); - } else { - /* Flush entire mapping when no address is specified. */ - virtual = args->mapping; - } - if ( args->size ) { - size = args->size; - } else { - /* Flush entire mapping when no size is specified. */ - size = mem->size_bytes - offset; - } - - if ( (offset + size) > mem->size_bytes ) { - DBG_MSG(1, ("Trying to flush more than the entire UMP allocation: offset: %u + size: %u > %u\n", offset, size, mem->size_bytes)); - goto msync_release_and_return; - } - - /* The actual cache flush - Implemented for each OS*/ - _ump_osk_msync( mem, virtual, offset, size, args->op, NULL); - -msync_release_and_return: - ump_dd_reference_release(mem); - return; -} - -void _ump_ukk_cache_operations_control(_ump_uk_cache_operations_control_s* args) -{ - ump_session_data * session_data; - ump_uk_cache_op_control op; - - DEBUG_ASSERT_POINTER( args ); - DEBUG_ASSERT_POINTER( args->ctx ); - - op = args->op; - session_data = (ump_session_data *)args->ctx; - - _mali_osk_mutex_wait(session_data->lock); - if ( op== _UMP_UK_CACHE_OP_START ) { - session_data->cache_operations_ongoing++; - DBG_MSG(4, ("Cache ops start\n" )); - if ( session_data->cache_operations_ongoing != 1 ) { - DBG_MSG(2, ("UMP: Number of simultanious cache control ops: %d\n", session_data->cache_operations_ongoing) ); - } - } else if ( op== _UMP_UK_CACHE_OP_FINISH ) { - DBG_MSG(4, ("Cache ops finish\n")); - session_data->cache_operations_ongoing--; -#if 0 - if ( session_data->has_pending_level1_cache_flush) { - /* This function will set has_pending_level1_cache_flush=0 */ - _ump_osk_msync( NULL, NULL, 0, 0, _UMP_UK_MSYNC_FLUSH_L1, session_data); - } -#endif - - /* to be on the safe side: always flush l1 cache when cache operations are done */ - _ump_osk_msync( NULL, NULL, 0, 0, _UMP_UK_MSYNC_FLUSH_L1, session_data); - DBG_MSG(4, ("Cache ops finish end\n" )); - } else { - DBG_MSG(1, ("Illegal call to %s at line %d\n", __FUNCTION__, __LINE__)); - } - _mali_osk_mutex_signal(session_data->lock); - -} - -void _ump_ukk_switch_hw_usage(_ump_uk_switch_hw_usage_s *args ) -{ - ump_dd_mem * mem = NULL; - ump_uk_user old_user; - ump_uk_msync_op cache_op = _UMP_UK_MSYNC_CLEAN_AND_INVALIDATE; - ump_session_data *session_data; - - DEBUG_ASSERT_POINTER( args ); - DEBUG_ASSERT_POINTER( args->ctx ); - - session_data = (ump_session_data *)args->ctx; - - _mali_osk_mutex_wait(device.secure_id_map_lock); - ump_descriptor_mapping_get(device.secure_id_map, (int)args->secure_id, (void**)&mem); - - if (NULL == mem) { - _mali_osk_mutex_signal(device.secure_id_map_lock); - DBG_MSG(1, ("Failed to look up mapping in _ump_ukk_switch_hw_usage(). ID: %u\n", (ump_secure_id)args->secure_id)); - return; - } - - old_user = mem->hw_device; - mem->hw_device = args->new_user; - - DBG_MSG(3, ("UMP[%02u] Switch usage Start New: %s Prev: %s.\n", (ump_secure_id)args->secure_id, args->new_user?"MALI":"CPU",old_user?"MALI":"CPU")); - - if ( ! mem->is_cached ) { - _mali_osk_mutex_signal(device.secure_id_map_lock); - DBG_MSG(3, ("UMP[%02u] Changing owner of uncached memory. Cache flushing not needed.\n", (ump_secure_id)args->secure_id)); - return; - } - - if ( old_user == args->new_user) { - _mali_osk_mutex_signal(device.secure_id_map_lock); - DBG_MSG(4, ("UMP[%02u] Setting the new_user equal to previous for. Cache flushing not needed.\n", (ump_secure_id)args->secure_id)); - return; - } - if ( - /* Previous AND new is both different from CPU */ - (old_user != _UMP_UK_USED_BY_CPU) && (args->new_user != _UMP_UK_USED_BY_CPU ) - ) { - _mali_osk_mutex_signal(device.secure_id_map_lock); - DBG_MSG(4, ("UMP[%02u] Previous and new user is not CPU. Cache flushing not needed.\n", (ump_secure_id)args->secure_id)); - return; - } - - if ( (old_user != _UMP_UK_USED_BY_CPU ) && (args->new_user==_UMP_UK_USED_BY_CPU) ) { - cache_op =_UMP_UK_MSYNC_INVALIDATE; - DBG_MSG(4, ("UMP[%02u] Cache invalidation needed\n", (ump_secure_id)args->secure_id)); -#ifdef UMP_SKIP_INVALIDATION -#error - _mali_osk_mutex_signal(device.secure_id_map_lock); - DBG_MSG(4, ("UMP[%02u] Performing Cache invalidation SKIPPED\n", (ump_secure_id)args->secure_id)); - return; -#endif - } - /* Ensure the memory doesn't dissapear when we are flushing it. */ - ump_dd_reference_add(mem); - _mali_osk_mutex_signal(device.secure_id_map_lock); - - /* Take lock to protect: session->cache_operations_ongoing and session->has_pending_level1_cache_flush */ - _mali_osk_mutex_wait(session_data->lock); - /* Actual cache flush */ - _ump_osk_msync( mem, NULL, 0, mem->size_bytes, cache_op, session_data); - _mali_osk_mutex_signal(session_data->lock); - - ump_dd_reference_release(mem); - DBG_MSG(4, ("UMP[%02u] Switch usage Finish\n", (ump_secure_id)args->secure_id)); - return; -} - -void _ump_ukk_lock(_ump_uk_lock_s *args ) -{ - ump_dd_mem * mem = NULL; - - _mali_osk_mutex_wait(device.secure_id_map_lock); - ump_descriptor_mapping_get(device.secure_id_map, (int)args->secure_id, (void**)&mem); - - if (NULL == mem) { - _mali_osk_mutex_signal(device.secure_id_map_lock); - DBG_MSG(1, ("UMP[%02u] Failed to look up mapping in _ump_ukk_lock(). ID: %u\n", (ump_secure_id)args->secure_id)); - return; - } - ump_dd_reference_add(mem); - _mali_osk_mutex_signal(device.secure_id_map_lock); - - DBG_MSG(1, ("UMP[%02u] Lock. New lock flag: %d. Old Lock flag:\n", (u32)args->secure_id, (u32)args->lock_usage, (u32) mem->lock_usage )); - - mem->lock_usage = (ump_lock_usage) args->lock_usage; - - ump_dd_reference_release(mem); -} - -void _ump_ukk_unlock(_ump_uk_unlock_s *args ) -{ - ump_dd_mem * mem = NULL; - - _mali_osk_mutex_wait(device.secure_id_map_lock); - ump_descriptor_mapping_get(device.secure_id_map, (int)args->secure_id, (void**)&mem); - - if (NULL == mem) { - _mali_osk_mutex_signal(device.secure_id_map_lock); - DBG_MSG(1, ("Failed to look up mapping in _ump_ukk_unlock(). ID: %u\n", (ump_secure_id)args->secure_id)); - return; - } - ump_dd_reference_add(mem); - _mali_osk_mutex_signal(device.secure_id_map_lock); - - DBG_MSG(1, ("UMP[%02u] Unlocking. Old Lock flag:\n", (u32)args->secure_id, (u32) mem->lock_usage )); - - mem->lock_usage = (ump_lock_usage) UMP_NOT_LOCKED; - - ump_dd_reference_release(mem); -} diff --git a/drivers/gpu/arm/ump/common/ump_kernel_common.c b/drivers/gpu/arm/ump/common/ump_kernel_common.c deleted file mode 100644 index e207eea0c59644..00000000000000 --- a/drivers/gpu/arm/ump/common/ump_kernel_common.c +++ /dev/null @@ -1,370 +0,0 @@ -/* - * Copyright (C) 2010-2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "mali_kernel_common.h" -#include "mali_osk.h" -#include "mali_osk_bitops.h" -#include "mali_osk_list.h" -#include "ump_osk.h" -#include "ump_uk_types.h" -#include "ump_ukk.h" -#include "ump_kernel_common.h" -#include "ump_kernel_descriptor_mapping.h" -#include "ump_kernel_memory_backend.h" - - - -/** - * Define the initial and maximum size of number of secure_ids on the system - */ -#define UMP_SECURE_ID_TABLE_ENTRIES_INITIAL (128 ) -#define UMP_SECURE_ID_TABLE_ENTRIES_MAXIMUM (4096 ) - - -/** - * Define the initial and maximum size of the ump_session_data::cookies_map, - * which is a \ref ump_descriptor_mapping. This limits how many secure_ids - * may be mapped into a particular process using _ump_ukk_map_mem(). - */ - -#define UMP_COOKIES_PER_SESSION_INITIAL (UMP_SECURE_ID_TABLE_ENTRIES_INITIAL ) -#define UMP_COOKIES_PER_SESSION_MAXIMUM (UMP_SECURE_ID_TABLE_ENTRIES_MAXIMUM) - -struct ump_dev device; - -_mali_osk_errcode_t ump_kernel_constructor(void) -{ - _mali_osk_errcode_t err; - - /* Perform OS Specific initialization */ - err = _ump_osk_init(); - if( _MALI_OSK_ERR_OK != err ) { - MSG_ERR(("Failed to initiaze the UMP Device Driver")); - return err; - } - - /* Init the global device */ - _mali_osk_memset(&device, 0, sizeof(device) ); - - /* Create the descriptor map, which will be used for mapping secure ID to ump_dd_mem structs */ - device.secure_id_map_lock = _mali_osk_mutex_init(_MALI_OSK_LOCKFLAG_UNORDERED, 0); - if (NULL == device.secure_id_map_lock) { - MSG_ERR(("Failed to create OSK lock for secure id lookup table\n")); - return _MALI_OSK_ERR_NOMEM; - } - - device.secure_id_map = ump_descriptor_mapping_create(UMP_SECURE_ID_TABLE_ENTRIES_INITIAL, UMP_SECURE_ID_TABLE_ENTRIES_MAXIMUM); - if (NULL == device.secure_id_map) { - _mali_osk_mutex_term(device.secure_id_map_lock); - MSG_ERR(("Failed to create secure id lookup table\n")); - return _MALI_OSK_ERR_NOMEM; - } - - /* Init memory backend */ - device.backend = ump_memory_backend_create(); - if (NULL == device.backend) { - MSG_ERR(("Failed to create memory backend\n")); - _mali_osk_mutex_term(device.secure_id_map_lock); - ump_descriptor_mapping_destroy(device.secure_id_map); - return _MALI_OSK_ERR_NOMEM; - } - - return _MALI_OSK_ERR_OK; -} - -void ump_kernel_destructor(void) -{ - DEBUG_ASSERT_POINTER(device.secure_id_map); - DEBUG_ASSERT_POINTER(device.secure_id_map_lock); - - _mali_osk_mutex_term(device.secure_id_map_lock); - device.secure_id_map_lock = NULL; - - ump_descriptor_mapping_destroy(device.secure_id_map); - device.secure_id_map = NULL; - - device.backend->shutdown(device.backend); - device.backend = NULL; - - ump_memory_backend_destroy(); - - _ump_osk_term(); -} - -/** Creates a new UMP session - */ -_mali_osk_errcode_t _ump_ukk_open( void** context ) -{ - struct ump_session_data * session_data; - - /* allocated struct to track this session */ - session_data = (struct ump_session_data *)_mali_osk_malloc(sizeof(struct ump_session_data)); - if (NULL == session_data) { - MSG_ERR(("Failed to allocate ump_session_data in ump_file_open()\n")); - return _MALI_OSK_ERR_NOMEM; - } - - session_data->lock = _mali_osk_mutex_init(_MALI_OSK_LOCKFLAG_UNORDERED, 0); - if( NULL == session_data->lock ) { - MSG_ERR(("Failed to initialize lock for ump_session_data in ump_file_open()\n")); - _mali_osk_free(session_data); - return _MALI_OSK_ERR_NOMEM; - } - - session_data->cookies_map = ump_descriptor_mapping_create( UMP_COOKIES_PER_SESSION_INITIAL, UMP_COOKIES_PER_SESSION_MAXIMUM ); - - if ( NULL == session_data->cookies_map ) { - MSG_ERR(("Failed to create descriptor mapping for _ump_ukk_map_mem cookies\n")); - - _mali_osk_mutex_term(session_data->lock); - _mali_osk_free( session_data ); - return _MALI_OSK_ERR_NOMEM; - } - - _MALI_OSK_INIT_LIST_HEAD(&session_data->list_head_session_memory_list); - - _MALI_OSK_INIT_LIST_HEAD(&session_data->list_head_session_memory_mappings_list); - - /* Since initial version of the UMP interface did not use the API_VERSION ioctl we have to assume - that it is this version, and not the "latest" one: UMP_IOCTL_API_VERSION - Current and later API versions would do an additional call to this IOCTL and update this variable - to the correct one.*/ - session_data->api_version = MAKE_VERSION_ID(1); - - *context = (void*)session_data; - - session_data->cache_operations_ongoing = 0 ; - session_data->has_pending_level1_cache_flush = 0; - - DBG_MSG(2, ("New session opened\n")); - - return _MALI_OSK_ERR_OK; -} - -_mali_osk_errcode_t _ump_ukk_close( void** context ) -{ - struct ump_session_data * session_data; - ump_session_memory_list_element * item; - ump_session_memory_list_element * tmp; - - session_data = (struct ump_session_data *)*context; - if (NULL == session_data) { - MSG_ERR(("Session data is NULL in _ump_ukk_close()\n")); - return _MALI_OSK_ERR_INVALID_ARGS; - } - - /* Unmap any descriptors mapped in. */ - if (0 == _mali_osk_list_empty(&session_data->list_head_session_memory_mappings_list)) { - ump_memory_allocation *descriptor; - ump_memory_allocation *temp; - - DBG_MSG(1, ("Memory mappings found on session usage list during session termination\n")); - - /* use the 'safe' list iterator, since freeing removes the active block from the list we're iterating */ - _MALI_OSK_LIST_FOREACHENTRY(descriptor, temp, &session_data->list_head_session_memory_mappings_list, ump_memory_allocation, list) { - _ump_uk_unmap_mem_s unmap_args; - DBG_MSG(4, ("Freeing block with phys address 0x%x size 0x%x mapped in user space at 0x%x\n", - descriptor->phys_addr, descriptor->size, descriptor->mapping)); - unmap_args.ctx = (void*)session_data; - unmap_args.mapping = descriptor->mapping; - unmap_args.size = descriptor->size; - unmap_args._ukk_private = NULL; /* NOTE: unused */ - unmap_args.cookie = descriptor->cookie; - - /* NOTE: This modifies the list_head_session_memory_mappings_list */ - _ump_ukk_unmap_mem( &unmap_args ); - } - } - - /* ASSERT that we really did free everything, because _ump_ukk_unmap_mem() - * can fail silently. */ - DEBUG_ASSERT( _mali_osk_list_empty(&session_data->list_head_session_memory_mappings_list) ); - - _MALI_OSK_LIST_FOREACHENTRY(item, tmp, &session_data->list_head_session_memory_list, ump_session_memory_list_element, list) { - _mali_osk_list_del(&item->list); - DBG_MSG(2, ("Releasing UMP memory %u as part of file close\n", item->mem->secure_id)); - ump_dd_reference_release(item->mem); - _mali_osk_free(item); - } - - ump_descriptor_mapping_destroy( session_data->cookies_map ); - - _mali_osk_mutex_term(session_data->lock); - _mali_osk_free(session_data); - - DBG_MSG(2, ("Session closed\n")); - - return _MALI_OSK_ERR_OK; -} - -_mali_osk_errcode_t _ump_ukk_map_mem( _ump_uk_map_mem_s *args ) -{ - struct ump_session_data * session_data; - ump_memory_allocation * descriptor; /* Describes current mapping of memory */ - _mali_osk_errcode_t err; - unsigned long offset = 0; - unsigned long left; - ump_dd_handle handle; /* The real UMP handle for this memory. Its real datatype is ump_dd_mem* */ - ump_dd_mem * mem; /* The real UMP memory. It is equal to the handle, but with exposed struct */ - u32 block; - int map_id; - - session_data = (ump_session_data *)args->ctx; - if( NULL == session_data ) { - MSG_ERR(("Session data is NULL in _ump_ukk_map_mem()\n")); - return _MALI_OSK_ERR_INVALID_ARGS; - } - - descriptor = (ump_memory_allocation*) _mali_osk_calloc( 1, sizeof(ump_memory_allocation)); - if (NULL == descriptor) { - MSG_ERR(("ump_ukk_map_mem: descriptor allocation failed\n")); - return _MALI_OSK_ERR_NOMEM; - } - - handle = ump_dd_handle_create_from_secure_id(args->secure_id); - if ( UMP_DD_HANDLE_INVALID == handle) { - _mali_osk_free(descriptor); - DBG_MSG(1, ("Trying to map unknown secure ID %u\n", args->secure_id)); - return _MALI_OSK_ERR_FAULT; - } - - mem = (ump_dd_mem*)handle; - DEBUG_ASSERT(mem); - if (mem->size_bytes != args->size) { - _mali_osk_free(descriptor); - ump_dd_reference_release(handle); - DBG_MSG(1, ("Trying to map too much or little. ID: %u, virtual size=%lu, UMP size: %lu\n", args->secure_id, args->size, mem->size_bytes)); - return _MALI_OSK_ERR_FAULT; - } - - map_id = ump_descriptor_mapping_allocate_mapping( session_data->cookies_map, (void*) descriptor ); - - if (map_id < 0) { - _mali_osk_free(descriptor); - ump_dd_reference_release(handle); - DBG_MSG(1, ("ump_ukk_map_mem: unable to allocate a descriptor_mapping for return cookie\n")); - - return _MALI_OSK_ERR_NOMEM; - } - - descriptor->size = args->size; - descriptor->handle = handle; - descriptor->phys_addr = args->phys_addr; - descriptor->process_mapping_info = args->_ukk_private; - descriptor->ump_session = session_data; - descriptor->cookie = (u32)map_id; - - if ( mem->is_cached ) { - descriptor->is_cached = 1; - args->is_cached = 1; - DBG_MSG(3, ("Mapping UMP secure_id: %d as cached.\n", args->secure_id)); - } else { - descriptor->is_cached = 0; - args->is_cached = 0; - DBG_MSG(3, ("Mapping UMP secure_id: %d as Uncached.\n", args->secure_id)); - } - - _mali_osk_list_init( &descriptor->list ); - - err = _ump_osk_mem_mapregion_init( descriptor ); - if( _MALI_OSK_ERR_OK != err ) { - DBG_MSG(1, ("Failed to initialize memory mapping in _ump_ukk_map_mem(). ID: %u\n", args->secure_id)); - ump_descriptor_mapping_free( session_data->cookies_map, map_id ); - _mali_osk_free(descriptor); - ump_dd_reference_release(mem); - return err; - } - - DBG_MSG(4, ("Mapping virtual to physical memory: ID: %u, size:%lu, first physical addr: 0x%08lx, number of regions: %lu\n", - mem->secure_id, - mem->size_bytes, - ((NULL != mem->block_array) ? mem->block_array->addr : 0), - mem->nr_blocks)); - - left = descriptor->size; - /* loop over all blocks and map them in */ - for (block = 0; block < mem->nr_blocks; block++) { - unsigned long size_to_map; - - if (left > mem->block_array[block].size) { - size_to_map = mem->block_array[block].size; - } else { - size_to_map = left; - } - - if (_MALI_OSK_ERR_OK != _ump_osk_mem_mapregion_map(descriptor, offset, (u32 *)&(mem->block_array[block].addr), size_to_map ) ) { - DBG_MSG(1, ("WARNING: _ump_ukk_map_mem failed to map memory into userspace\n")); - ump_descriptor_mapping_free( session_data->cookies_map, map_id ); - ump_dd_reference_release(mem); - _ump_osk_mem_mapregion_term( descriptor ); - _mali_osk_free(descriptor); - return _MALI_OSK_ERR_FAULT; - } - left -= size_to_map; - offset += size_to_map; - } - - /* Add to the ump_memory_allocation tracking list */ - _mali_osk_mutex_wait(session_data->lock); - _mali_osk_list_add( &descriptor->list, &session_data->list_head_session_memory_mappings_list ); - _mali_osk_mutex_signal(session_data->lock); - - args->mapping = descriptor->mapping; - args->cookie = descriptor->cookie; - - return _MALI_OSK_ERR_OK; -} - -void _ump_ukk_unmap_mem( _ump_uk_unmap_mem_s *args ) -{ - struct ump_session_data * session_data; - ump_memory_allocation * descriptor; - ump_dd_handle handle; - - session_data = (ump_session_data *)args->ctx; - - if( NULL == session_data ) { - MSG_ERR(("Session data is NULL in _ump_ukk_map_mem()\n")); - return; - } - - if (0 != ump_descriptor_mapping_get( session_data->cookies_map, (int)args->cookie, (void**)&descriptor) ) { - MSG_ERR(("_ump_ukk_map_mem: cookie 0x%X not found for this session\n", args->cookie )); - return; - } - - DEBUG_ASSERT_POINTER(descriptor); - - handle = descriptor->handle; - if ( UMP_DD_HANDLE_INVALID == handle) { - DBG_MSG(1, ("WARNING: Trying to unmap unknown handle: UNKNOWN\n")); - return; - } - - /* Remove the ump_memory_allocation from the list of tracked mappings */ - _mali_osk_mutex_wait(session_data->lock); - _mali_osk_list_del( &descriptor->list ); - _mali_osk_mutex_signal(session_data->lock); - - ump_descriptor_mapping_free( session_data->cookies_map, (int)args->cookie ); - - ump_dd_reference_release(handle); - - _ump_osk_mem_mapregion_term( descriptor ); - _mali_osk_free(descriptor); -} - -u32 _ump_ukk_report_memory_usage( void ) -{ - if(device.backend->stat) - return device.backend->stat(device.backend); - else - return 0; -} diff --git a/drivers/gpu/arm/ump/common/ump_kernel_common.h b/drivers/gpu/arm/ump/common/ump_kernel_common.h deleted file mode 100644 index 49cea7ee7477e6..00000000000000 --- a/drivers/gpu/arm/ump/common/ump_kernel_common.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2010-2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef __UMP_KERNEL_COMMON_H__ -#define __UMP_KERNEL_COMMON_H__ - -#include "ump_kernel_types.h" -#include -#include "ump_kernel_descriptor_mapping.h" -#include "ump_kernel_memory_backend.h" - - -#ifdef DEBUG -extern int ump_debug_level; -#define UMP_DEBUG_PRINT(args) _mali_osk_dbgmsg args -#define UMP_DEBUG_CODE(args) args -#define DBG_MSG(level,args) do { /* args should be in brackets */ \ - ((level) <= ump_debug_level)?\ - UMP_DEBUG_PRINT(("UMP<" #level ">: ")), \ - UMP_DEBUG_PRINT(args):0; \ - } while (0) - -#define DBG_MSG_IF(level,condition,args) /* args should be in brackets */ \ - if((condition)&&((level) <= ump_debug_level)) {\ - UMP_DEBUG_PRINT(("UMP<" #level ">: ")); \ - UMP_DEBUG_PRINT(args); \ - } - -#define DBG_MSG_ELSE(level,args) /* args should be in brackets */ \ - else if((level) <= ump_debug_level) { \ - UMP_DEBUG_PRINT(("UMP<" #level ">: ")); \ - UMP_DEBUG_PRINT(args); \ - } - -#define DEBUG_ASSERT_POINTER(pointer) do {if( (pointer)== NULL) MSG_ERR(("NULL pointer " #pointer)); } while(0) -#define DEBUG_ASSERT(condition) do {if(!(condition)) MSG_ERR(("ASSERT failed: " #condition)); } while(0) -#else /* DEBUG */ -#define UMP_DEBUG_PRINT(args) do {} while(0) -#define UMP_DEBUG_CODE(args) -#define DBG_MSG(level,args) do {} while(0) -#define DBG_MSG_IF(level,condition,args) do {} while(0) -#define DBG_MSG_ELSE(level,args) do {} while(0) -#define DEBUG_ASSERT(condition) do {} while(0) -#define DEBUG_ASSERT_POINTER(pointer) do {} while(0) -#endif /* DEBUG */ - -#define MSG_ERR(args) do{ /* args should be in brackets */ \ - _mali_osk_dbgmsg("UMP: ERR: %s\n" ,__FILE__); \ - _mali_osk_dbgmsg( " %s()%4d\n", __FUNCTION__, __LINE__) ; \ - _mali_osk_dbgmsg args ; \ - _mali_osk_dbgmsg("\n"); \ - } while(0) - -#define MSG(args) do{ /* args should be in brackets */ \ - _mali_osk_dbgmsg("UMP: "); \ - _mali_osk_dbgmsg args; \ - } while (0) - - - -/* - * This struct is used to store per session data. - * A session is created when someone open() the device, and - * closed when someone close() it or the user space application terminates. - */ -typedef struct ump_session_data { - _mali_osk_list_t list_head_session_memory_list; /**< List of ump allocations made by the process (elements are ump_session_memory_list_element) */ - _mali_osk_list_t list_head_session_memory_mappings_list; /**< List of ump_memory_allocations mapped in */ - int api_version; - _mali_osk_mutex_t *lock; - ump_descriptor_mapping * cookies_map; /**< Secure mapping of cookies from _ump_ukk_map_mem() */ - int cache_operations_ongoing; - int has_pending_level1_cache_flush; -} ump_session_data; - - - -/* - * This struct is used to track the UMP memory references a session has. - * We need to track this in order to be able to clean up after user space processes - * which don't do it themself (e.g. due to a crash or premature termination). - */ -typedef struct ump_session_memory_list_element { - struct ump_dd_mem * mem; - _mali_osk_list_t list; -} ump_session_memory_list_element; - - - -/* - * Device specific data, created when device driver is loaded, and then kept as the global variable device. - */ -typedef struct ump_dev { - _mali_osk_mutex_t *secure_id_map_lock; - ump_descriptor_mapping * secure_id_map; - ump_memory_backend * backend; -} ump_dev; - - - -extern int ump_debug_level; -extern struct ump_dev device; - -_mali_osk_errcode_t ump_kernel_constructor(void); -void ump_kernel_destructor(void); -int map_errcode( _mali_osk_errcode_t err ); - -/** - * variables from user space cannot be dereferenced from kernel space; tagging them - * with __user allows the GCC compiler to generate a warning. Other compilers may - * not support this so we define it here as an empty macro if the compiler doesn't - * define it. - */ -#ifndef __user -#define __user -#endif - -#endif /* __UMP_KERNEL_COMMON_H__ */ diff --git a/drivers/gpu/arm/ump/common/ump_kernel_descriptor_mapping.c b/drivers/gpu/arm/ump/common/ump_kernel_descriptor_mapping.c deleted file mode 100644 index ab4e01e012019c..00000000000000 --- a/drivers/gpu/arm/ump/common/ump_kernel_descriptor_mapping.c +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (C) 2010-2011, 2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "mali_kernel_common.h" -#include "mali_osk.h" -#include "mali_osk_bitops.h" -#include "ump_kernel_common.h" -#include "ump_kernel_descriptor_mapping.h" - -#define MALI_PAD_INT(x) (((x) + (BITS_PER_LONG - 1)) & ~(BITS_PER_LONG - 1)) - -/** - * Allocate a descriptor table capable of holding 'count' mappings - * @param count Number of mappings in the table - * @return Pointer to a new table, NULL on error - */ -static ump_descriptor_table * descriptor_table_alloc(int count); - -/** - * Free a descriptor table - * @param table The table to free - */ -static void descriptor_table_free(ump_descriptor_table * table); - -ump_descriptor_mapping * ump_descriptor_mapping_create(int init_entries, int max_entries) -{ - ump_descriptor_mapping * map = _mali_osk_calloc(1, sizeof(ump_descriptor_mapping) ); - - init_entries = MALI_PAD_INT(init_entries); - max_entries = MALI_PAD_INT(max_entries); - - if (NULL != map) { - map->table = descriptor_table_alloc(init_entries); - if (NULL != map->table) { - map->lock = _mali_osk_mutex_rw_init(_MALI_OSK_LOCKFLAG_UNORDERED, 0); - if ( NULL != map->lock ) { - _mali_osk_set_nonatomic_bit(0, map->table->usage); /* reserve bit 0 to prevent NULL/zero logic to kick in */ - map->max_nr_mappings_allowed = max_entries; - map->current_nr_mappings = init_entries; - return map; - } - descriptor_table_free(map->table); - } - _mali_osk_free(map); - } - return NULL; -} - -void ump_descriptor_mapping_destroy(ump_descriptor_mapping * map) -{ - descriptor_table_free(map->table); - _mali_osk_mutex_rw_term(map->lock); - _mali_osk_free(map); -} - -int ump_descriptor_mapping_allocate_mapping(ump_descriptor_mapping * map, void * target) -{ - int descriptor = -1;/*-EFAULT;*/ - _mali_osk_mutex_rw_wait(map->lock, _MALI_OSK_LOCKMODE_RW); - descriptor = _mali_osk_find_first_zero_bit(map->table->usage, map->current_nr_mappings); - if (descriptor == map->current_nr_mappings) { - int nr_mappings_new; - /* no free descriptor, try to expand the table */ - ump_descriptor_table * new_table; - ump_descriptor_table * old_table = map->table; - nr_mappings_new= map->current_nr_mappings *2; - - if (map->current_nr_mappings >= map->max_nr_mappings_allowed) { - descriptor = -1; - goto unlock_and_exit; - } - - new_table = descriptor_table_alloc(nr_mappings_new); - if (NULL == new_table) { - descriptor = -1; - goto unlock_and_exit; - } - - _mali_osk_memcpy(new_table->usage, old_table->usage, (sizeof(unsigned long)*map->current_nr_mappings) / BITS_PER_LONG); - _mali_osk_memcpy(new_table->mappings, old_table->mappings, map->current_nr_mappings * sizeof(void*)); - map->table = new_table; - map->current_nr_mappings = nr_mappings_new; - descriptor_table_free(old_table); - } - - /* we have found a valid descriptor, set the value and usage bit */ - _mali_osk_set_nonatomic_bit(descriptor, map->table->usage); - map->table->mappings[descriptor] = target; - -unlock_and_exit: - _mali_osk_mutex_rw_signal(map->lock, _MALI_OSK_LOCKMODE_RW); - return descriptor; -} - -int ump_descriptor_mapping_get(ump_descriptor_mapping * map, int descriptor, void** target) -{ - int result = -1;/*-EFAULT;*/ - DEBUG_ASSERT(map); - _mali_osk_mutex_rw_wait(map->lock, _MALI_OSK_LOCKMODE_RO); - if ((descriptor > 0) && (descriptor < map->current_nr_mappings) && _mali_osk_test_bit(descriptor, map->table->usage)) { - *target = map->table->mappings[descriptor]; - result = 0; - } else *target = NULL; - _mali_osk_mutex_rw_signal(map->lock, _MALI_OSK_LOCKMODE_RO); - return result; -} - -int ump_descriptor_mapping_set(ump_descriptor_mapping * map, int descriptor, void * target) -{ - int result = -1;/*-EFAULT;*/ - _mali_osk_mutex_rw_wait(map->lock, _MALI_OSK_LOCKMODE_RO); - if ((descriptor > 0) && (descriptor < map->current_nr_mappings) && _mali_osk_test_bit(descriptor, map->table->usage)) { - map->table->mappings[descriptor] = target; - result = 0; - } - _mali_osk_mutex_rw_signal(map->lock, _MALI_OSK_LOCKMODE_RO); - return result; -} - -void ump_descriptor_mapping_free(ump_descriptor_mapping * map, int descriptor) -{ - _mali_osk_mutex_rw_wait(map->lock, _MALI_OSK_LOCKMODE_RW); - if ((descriptor > 0) && (descriptor < map->current_nr_mappings) && _mali_osk_test_bit(descriptor, map->table->usage)) { - map->table->mappings[descriptor] = NULL; - _mali_osk_clear_nonatomic_bit(descriptor, map->table->usage); - } - _mali_osk_mutex_rw_signal(map->lock, _MALI_OSK_LOCKMODE_RW); -} - -static ump_descriptor_table * descriptor_table_alloc(int count) -{ - ump_descriptor_table * table; - - table = _mali_osk_calloc(1, sizeof(ump_descriptor_table) + ((sizeof(unsigned long) * count)/BITS_PER_LONG) + (sizeof(void*) * count) ); - - if (NULL != table) { - table->usage = (u32*)((u8*)table + sizeof(ump_descriptor_table)); - table->mappings = (void**)((u8*)table + sizeof(ump_descriptor_table) + ((sizeof(unsigned long) * count)/BITS_PER_LONG)); - } - - return table; -} - -static void descriptor_table_free(ump_descriptor_table * table) -{ - _mali_osk_free(table); -} - diff --git a/drivers/gpu/arm/ump/common/ump_kernel_descriptor_mapping.h b/drivers/gpu/arm/ump/common/ump_kernel_descriptor_mapping.h deleted file mode 100644 index a351d3ae311743..00000000000000 --- a/drivers/gpu/arm/ump/common/ump_kernel_descriptor_mapping.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2010-2011, 2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** - * @file ump_kernel_descriptor_mapping.h - */ - -#ifndef __UMP_KERNEL_DESCRIPTOR_MAPPING_H__ -#define __UMP_KERNEL_DESCRIPTOR_MAPPING_H__ - -#include "mali_osk.h" - -/** - * The actual descriptor mapping table, never directly accessed by clients - */ -typedef struct ump_descriptor_table { - u32 * usage; /**< Pointer to bitpattern indicating if a descriptor is valid/used or not */ - void** mappings; /**< Array of the pointers the descriptors map to */ -} ump_descriptor_table; - -/** - * The descriptor mapping object - * Provides a separate namespace where we can map an integer to a pointer - */ -typedef struct ump_descriptor_mapping { - _mali_osk_mutex_rw_t *lock; /**< Lock protecting access to the mapping object */ - int max_nr_mappings_allowed; /**< Max number of mappings to support in this namespace */ - int current_nr_mappings; /**< Current number of possible mappings */ - ump_descriptor_table * table; /**< Pointer to the current mapping table */ -} ump_descriptor_mapping; - -/** - * Create a descriptor mapping object - * Create a descriptor mapping capable of holding init_entries growable to max_entries - * @param init_entries Number of entries to preallocate memory for - * @param max_entries Number of entries to max support - * @return Pointer to a descriptor mapping object, NULL on failure - */ -ump_descriptor_mapping * ump_descriptor_mapping_create(int init_entries, int max_entries); - -/** - * Destroy a descriptor mapping object - * @param map The map to free - */ -void ump_descriptor_mapping_destroy(ump_descriptor_mapping * map); - -/** - * Allocate a new mapping entry (descriptor ID) - * Allocates a new entry in the map. - * @param map The map to allocate a new entry in - * @param target The value to map to - * @return The descriptor allocated, a negative value on error - */ -int ump_descriptor_mapping_allocate_mapping(ump_descriptor_mapping * map, void * target); - -/** - * Get the value mapped to by a descriptor ID - * @param map The map to lookup the descriptor id in - * @param descriptor The descriptor ID to lookup - * @param target Pointer to a pointer which will receive the stored value - * @return 0 on successful lookup, negative on error - */ -int ump_descriptor_mapping_get(ump_descriptor_mapping * map, int descriptor, void** target); - -/** - * Set the value mapped to by a descriptor ID - * @param map The map to lookup the descriptor id in - * @param descriptor The descriptor ID to lookup - * @param target Pointer to replace the current value with - * @return 0 on successful lookup, negative on error - */ -int ump_descriptor_mapping_set(ump_descriptor_mapping * map, int descriptor, void * target); - -/** - * Free the descriptor ID - * For the descriptor to be reused it has to be freed - * @param map The map to free the descriptor from - * @param descriptor The descriptor ID to free - */ -void ump_descriptor_mapping_free(ump_descriptor_mapping * map, int descriptor); - -#endif /* __UMP_KERNEL_DESCRIPTOR_MAPPING_H__ */ diff --git a/drivers/gpu/arm/ump/common/ump_kernel_memory_backend.h b/drivers/gpu/arm/ump/common/ump_kernel_memory_backend.h deleted file mode 100644 index 887a2c7d89d74b..00000000000000 --- a/drivers/gpu/arm/ump/common/ump_kernel_memory_backend.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2010-2011, 2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** - * @file ump_kernel_memory_mapping.h - */ - -#ifndef __UMP_KERNEL_MEMORY_BACKEND_H__ -#define __UMP_KERNEL_MEMORY_BACKEND_H__ - -#include -#include "ump_kernel_types.h" - - -typedef struct ump_memory_allocation { - void * phys_addr; - void * mapping; - unsigned long size; - ump_dd_handle handle; - void * process_mapping_info; - u32 cookie; /**< necessary on some U/K interface implementations */ - struct ump_session_data * ump_session; /**< Session that this allocation belongs to */ - _mali_osk_list_t list; /**< List for linking together memory allocations into the session's memory head */ - u32 is_cached; -} ump_memory_allocation; - -typedef struct ump_memory_backend { - int (*allocate)(void* ctx, ump_dd_mem * descriptor); - void (*release)(void* ctx, ump_dd_mem * descriptor); - void (*shutdown)(struct ump_memory_backend * backend); - u32 (*stat)(struct ump_memory_backend *backend); - int (*pre_allocate_physical_check)(void *ctx, u32 size); - u32 (*adjust_to_mali_phys)(void *ctx, u32 cpu_phys); - void * ctx; -} ump_memory_backend; - -ump_memory_backend * ump_memory_backend_create ( void ); -void ump_memory_backend_destroy( void ); - -#endif /*__UMP_KERNEL_MEMORY_BACKEND_H__ */ - diff --git a/drivers/gpu/arm/ump/common/ump_kernel_ref_drv.c b/drivers/gpu/arm/ump/common/ump_kernel_ref_drv.c deleted file mode 100644 index 14320fc86bd7cb..00000000000000 --- a/drivers/gpu/arm/ump/common/ump_kernel_ref_drv.c +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright (C) 2010-2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "mali_osk.h" -#include "mali_osk_list.h" -#include "ump_osk.h" -#include "ump_uk_types.h" - -#include -#include "ump_kernel_common.h" -#include "ump_kernel_descriptor_mapping.h" - -#define UMP_MINIMUM_SIZE 4096 -#define UMP_MINIMUM_SIZE_MASK (~(UMP_MINIMUM_SIZE-1)) -#define UMP_SIZE_ALIGN(x) (((x)+UMP_MINIMUM_SIZE-1)&UMP_MINIMUM_SIZE_MASK) -#define UMP_ADDR_ALIGN_OFFSET(x) ((x)&(UMP_MINIMUM_SIZE-1)) -static void phys_blocks_release(void * ctx, struct ump_dd_mem * descriptor); - -UMP_KERNEL_API_EXPORT ump_dd_handle ump_dd_handle_create_from_phys_blocks(ump_dd_physical_block * blocks, unsigned long num_blocks) -{ - ump_dd_mem * mem; - unsigned long size_total = 0; - int map_id; - u32 i; - - /* Go through the input blocks and verify that they are sane */ - for (i=0; i < num_blocks; i++) { - unsigned long addr = blocks[i].addr; - unsigned long size = blocks[i].size; - - DBG_MSG(5, ("Adding physical memory to new handle. Address: 0x%08lx, size: %lu\n", addr, size)); - size_total += blocks[i].size; - - if (0 != UMP_ADDR_ALIGN_OFFSET(addr)) { - MSG_ERR(("Trying to create UMP memory from unaligned physical address. Address: 0x%08lx\n", addr)); - return UMP_DD_HANDLE_INVALID; - } - - if (0 != UMP_ADDR_ALIGN_OFFSET(size)) { - MSG_ERR(("Trying to create UMP memory with unaligned size. Size: %lu\n", size)); - return UMP_DD_HANDLE_INVALID; - } - } - - /* Allocate the ump_dd_mem struct for this allocation */ - mem = _mali_osk_malloc(sizeof(*mem)); - if (NULL == mem) { - DBG_MSG(1, ("Could not allocate ump_dd_mem in ump_dd_handle_create_from_phys_blocks()\n")); - return UMP_DD_HANDLE_INVALID; - } - - /* Find a secure ID for this allocation */ - _mali_osk_mutex_wait(device.secure_id_map_lock); - map_id = ump_descriptor_mapping_allocate_mapping(device.secure_id_map, (void*) mem); - - if (map_id < 0) { - _mali_osk_mutex_signal(device.secure_id_map_lock); - _mali_osk_free(mem); - DBG_MSG(1, ("Failed to allocate secure ID in ump_dd_handle_create_from_phys_blocks()\n")); - return UMP_DD_HANDLE_INVALID; - } - - /* Now, make a copy of the block information supplied by the user */ - mem->block_array = _mali_osk_malloc(sizeof(ump_dd_physical_block)* num_blocks); - if (NULL == mem->block_array) { - ump_descriptor_mapping_free(device.secure_id_map, map_id); - _mali_osk_mutex_signal(device.secure_id_map_lock); - _mali_osk_free(mem); - DBG_MSG(1, ("Could not allocate a mem handle for function ump_dd_handle_create_from_phys_blocks().\n")); - return UMP_DD_HANDLE_INVALID; - } - - _mali_osk_memcpy(mem->block_array, blocks, sizeof(ump_dd_physical_block) * num_blocks); - - /* And setup the rest of the ump_dd_mem struct */ - _mali_osk_atomic_init(&mem->ref_count, 1); - mem->secure_id = (ump_secure_id)map_id; - mem->size_bytes = size_total; - mem->nr_blocks = num_blocks; - mem->backend_info = NULL; - mem->ctx = NULL; - mem->release_func = phys_blocks_release; - /* For now UMP handles created by ump_dd_handle_create_from_phys_blocks() is forced to be Uncached */ - mem->is_cached = 0; - mem->hw_device = _UMP_UK_USED_BY_CPU; - mem->lock_usage = UMP_NOT_LOCKED; - - _mali_osk_mutex_signal(device.secure_id_map_lock); - DBG_MSG(3, ("UMP memory created. ID: %u, size: %lu\n", mem->secure_id, mem->size_bytes)); - - return (ump_dd_handle)mem; -} - -static void phys_blocks_release(void * ctx, struct ump_dd_mem * descriptor) -{ - _mali_osk_free(descriptor->block_array); - descriptor->block_array = NULL; -} - -_mali_osk_errcode_t _ump_ukk_allocate( _ump_uk_allocate_s *user_interaction ) -{ - ump_session_data * session_data = NULL; - ump_dd_mem *new_allocation = NULL; - ump_session_memory_list_element * session_memory_element = NULL; - int map_id; - - DEBUG_ASSERT_POINTER( user_interaction ); - DEBUG_ASSERT_POINTER( user_interaction->ctx ); - - session_data = (ump_session_data *) user_interaction->ctx; - - session_memory_element = _mali_osk_calloc( 1, sizeof(ump_session_memory_list_element)); - if (NULL == session_memory_element) { - DBG_MSG(1, ("Failed to allocate ump_session_memory_list_element in ump_ioctl_allocate()\n")); - return _MALI_OSK_ERR_NOMEM; - } - - - new_allocation = _mali_osk_calloc( 1, sizeof(ump_dd_mem)); - if (NULL==new_allocation) { - _mali_osk_free(session_memory_element); - DBG_MSG(1, ("Failed to allocate ump_dd_mem in _ump_ukk_allocate()\n")); - return _MALI_OSK_ERR_NOMEM; - } - - /* Create a secure ID for this allocation */ - _mali_osk_mutex_wait(device.secure_id_map_lock); - map_id = ump_descriptor_mapping_allocate_mapping(device.secure_id_map, (void*)new_allocation); - - if (map_id < 0) { - _mali_osk_mutex_signal(device.secure_id_map_lock); - _mali_osk_free(session_memory_element); - _mali_osk_free(new_allocation); - DBG_MSG(1, ("Failed to allocate secure ID in ump_ioctl_allocate()\n")); - return - _MALI_OSK_ERR_INVALID_FUNC; - } - - /* Initialize the part of the new_allocation that we know so for */ - new_allocation->secure_id = (ump_secure_id)map_id; - _mali_osk_atomic_init(&new_allocation->ref_count,1); - if ( 0==(UMP_REF_DRV_UK_CONSTRAINT_USE_CACHE & user_interaction->constraints) ) - new_allocation->is_cached = 0; - else new_allocation->is_cached = 1; - - /* special case a size of 0, we should try to emulate what malloc does in this case, which is to return a valid pointer that must be freed, but can't be dereferences */ - if (0 == user_interaction->size) { - user_interaction->size = 1; /* emulate by actually allocating the minimum block size */ - } - - new_allocation->size_bytes = UMP_SIZE_ALIGN(user_interaction->size); /* Page align the size */ - new_allocation->lock_usage = UMP_NOT_LOCKED; - - /* Now, ask the active memory backend to do the actual memory allocation */ - if (!device.backend->allocate( device.backend->ctx, new_allocation ) ) { - DBG_MSG(3, ("OOM: No more UMP memory left. Failed to allocate memory in ump_ioctl_allocate(). Size: %lu, requested size: %lu\n", new_allocation->size_bytes, (unsigned long)user_interaction->size)); - ump_descriptor_mapping_free(device.secure_id_map, map_id); - _mali_osk_mutex_signal(device.secure_id_map_lock); - _mali_osk_free(new_allocation); - _mali_osk_free(session_memory_element); - return _MALI_OSK_ERR_INVALID_FUNC; - } - new_allocation->hw_device = _UMP_UK_USED_BY_CPU; - new_allocation->ctx = device.backend->ctx; - new_allocation->release_func = device.backend->release; - - _mali_osk_mutex_signal(device.secure_id_map_lock); - - /* Initialize the session_memory_element, and add it to the session object */ - session_memory_element->mem = new_allocation; - _mali_osk_mutex_wait(session_data->lock); - _mali_osk_list_add(&(session_memory_element->list), &(session_data->list_head_session_memory_list)); - _mali_osk_mutex_signal(session_data->lock); - - user_interaction->secure_id = new_allocation->secure_id; - user_interaction->size = new_allocation->size_bytes; - DBG_MSG(3, ("UMP memory allocated. ID: %u, size: %lu\n", new_allocation->secure_id, new_allocation->size_bytes)); - - return _MALI_OSK_ERR_OK; -} diff --git a/drivers/gpu/arm/ump/common/ump_kernel_types.h b/drivers/gpu/arm/ump/common/ump_kernel_types.h deleted file mode 100644 index 081fab3caf3204..00000000000000 --- a/drivers/gpu/arm/ump/common/ump_kernel_types.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2010-2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef __UMP_KERNEL_TYPES_H__ -#define __UMP_KERNEL_TYPES_H__ - -#include -#include "mali_osk.h" - - -typedef enum { - UMP_USED_BY_CPU = 0, - UMP_USED_BY_MALI = 1, - UMP_USED_BY_UNKNOWN_DEVICE= 100, -} ump_hw_usage; - -typedef enum { - UMP_NOT_LOCKED = 0, - UMP_READ = 1, - UMP_READ_WRITE = 3, -} ump_lock_usage; - - -/* - * This struct is what is "behind" a ump_dd_handle - */ -typedef struct ump_dd_mem { - ump_secure_id secure_id; - _mali_osk_atomic_t ref_count; - unsigned long size_bytes; - unsigned long nr_blocks; - ump_dd_physical_block * block_array; - void (*release_func)(void * ctx, struct ump_dd_mem * descriptor); - void * ctx; - void * backend_info; - int is_cached; - ump_hw_usage hw_device; - ump_lock_usage lock_usage; -} ump_dd_mem; - - - -#endif /* __UMP_KERNEL_TYPES_H__ */ diff --git a/drivers/gpu/arm/ump/common/ump_osk.h b/drivers/gpu/arm/ump/common/ump_osk.h deleted file mode 100644 index 9b048aa312b18d..00000000000000 --- a/drivers/gpu/arm/ump/common/ump_osk.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2010-2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** - * @file ump_osk.h - * Defines the OS abstraction layer for the UMP kernel device driver (OSK) - */ - -#ifndef __UMP_OSK_H__ -#define __UMP_OSK_H__ - -#include -#include -#include "ump_uk_types.h" -#include "ump_kernel_common.h" - -#ifdef __cplusplus -extern "C" { -#endif - -_mali_osk_errcode_t _ump_osk_init( void ); - -_mali_osk_errcode_t _ump_osk_term( void ); - -int _ump_osk_atomic_inc_and_read( _mali_osk_atomic_t *atom ); - -int _ump_osk_atomic_dec_and_read( _mali_osk_atomic_t *atom ); - -_mali_osk_errcode_t _ump_osk_mem_mapregion_init( ump_memory_allocation *descriptor ); - -_mali_osk_errcode_t _ump_osk_mem_mapregion_map( ump_memory_allocation * descriptor, u32 offset, u32 * phys_addr, unsigned long size ); - -void _ump_osk_mem_mapregion_term( ump_memory_allocation * descriptor ); - -void _ump_osk_msync( ump_dd_mem * mem, void * virt, u32 offset, u32 size, ump_uk_msync_op op, ump_session_data * session_data ); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/drivers/gpu/arm/ump/common/ump_uk_types.h b/drivers/gpu/arm/ump/common/ump_uk_types.h deleted file mode 100644 index ead12584cddb28..00000000000000 --- a/drivers/gpu/arm/ump/common/ump_uk_types.h +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (C) 2010, 2012-2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** - * @file ump_uk_types.h - * Defines the types and constants used in the user-kernel interface - */ - -#ifndef __UMP_UK_TYPES_H__ -#define __UMP_UK_TYPES_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -/* Helpers for API version handling */ -#define MAKE_VERSION_ID(x) (((x) << 16UL) | (x)) -#define IS_VERSION_ID(x) (((x) & 0xFFFF) == (((x) >> 16UL) & 0xFFFF)) -#define GET_VERSION(x) (((x) >> 16UL) & 0xFFFF) -#define IS_API_MATCH(x, y) (IS_VERSION_ID((x)) && IS_VERSION_ID((y)) && (GET_VERSION((x)) == GET_VERSION((y)))) - -/** - * API version define. - * Indicates the version of the kernel API - * The version is a 16bit integer incremented on each API change. - * The 16bit integer is stored twice in a 32bit integer - * So for version 1 the value would be 0x00010001 - */ -#define UMP_IOCTL_API_VERSION MAKE_VERSION_ID(2) - -typedef enum -{ - _UMP_IOC_QUERY_API_VERSION = 1, - _UMP_IOC_ALLOCATE, - _UMP_IOC_RELEASE, - _UMP_IOC_SIZE_GET, - _UMP_IOC_MAP_MEM, /* not used in Linux */ - _UMP_IOC_UNMAP_MEM, /* not used in Linux */ - _UMP_IOC_MSYNC, - _UMP_IOC_CACHE_OPERATIONS_CONTROL, - _UMP_IOC_SWITCH_HW_USAGE, - _UMP_IOC_LOCK, - _UMP_IOC_UNLOCK, -} _ump_uk_functions; - -typedef enum -{ - UMP_REF_DRV_UK_CONSTRAINT_NONE = 0, - UMP_REF_DRV_UK_CONSTRAINT_PHYSICALLY_LINEAR = 1, - UMP_REF_DRV_UK_CONSTRAINT_USE_CACHE = 4, -} ump_uk_alloc_constraints; - -typedef enum -{ - _UMP_UK_MSYNC_CLEAN = 0, - _UMP_UK_MSYNC_CLEAN_AND_INVALIDATE = 1, - _UMP_UK_MSYNC_INVALIDATE = 2, - _UMP_UK_MSYNC_FLUSH_L1 = 3, - _UMP_UK_MSYNC_READOUT_CACHE_ENABLED = 128, -} ump_uk_msync_op; - -typedef enum -{ - _UMP_UK_CACHE_OP_START = 0, - _UMP_UK_CACHE_OP_FINISH = 1, -} ump_uk_cache_op_control; - -typedef enum -{ - _UMP_UK_READ = 1, - _UMP_UK_READ_WRITE = 3, -} ump_uk_lock_usage; - -typedef enum -{ - _UMP_UK_USED_BY_CPU = 0, - _UMP_UK_USED_BY_MALI = 1, - _UMP_UK_USED_BY_UNKNOWN_DEVICE = 100, -} ump_uk_user; - -/** - * Get API version ([in,out] u32 api_version, [out] u32 compatible) - */ -typedef struct _ump_uk_api_version_s -{ - void *ctx; /**< [in,out] user-kernel context (trashed on output) */ - u32 version; /**< Set to the user space version on entry, stores the device driver version on exit */ - u32 compatible; /**< Non-null if the device is compatible with the client */ -} _ump_uk_api_version_s; - -/** - * ALLOCATE ([out] u32 secure_id, [in,out] u32 size, [in] contraints) - */ -typedef struct _ump_uk_allocate_s -{ - void *ctx; /**< [in,out] user-kernel context (trashed on output) */ - u32 secure_id; /**< Return value from DD to Userdriver */ - u32 size; /**< Input and output. Requested size; input. Returned size; output */ - ump_uk_alloc_constraints constraints; /**< Only input to Devicedriver */ -} _ump_uk_allocate_s; - -/** - * SIZE_GET ([in] u32 secure_id, [out]size ) - */ -typedef struct _ump_uk_size_get_s -{ - void *ctx; /**< [in,out] user-kernel context (trashed on output) */ - u32 secure_id; /**< Input to DD */ - u32 size; /**< Returned size; output */ -} _ump_uk_size_get_s; - -/** - * Release ([in] u32 secure_id) - */ -typedef struct _ump_uk_release_s -{ - void *ctx; /**< [in,out] user-kernel context (trashed on output) */ - u32 secure_id; /**< Input to DD */ -} _ump_uk_release_s; - -typedef struct _ump_uk_map_mem_s -{ - void *ctx; /**< [in,out] user-kernel context (trashed on output) */ - void *mapping; /**< [out] Returns user-space virtual address for the mapping */ - void *phys_addr; /**< [in] physical address */ - unsigned long size; /**< [in] size */ - u32 secure_id; /**< [in] secure_id to assign to mapping */ - void *_ukk_private; /**< Only used inside linux port between kernel frontend and common part to store vma */ - u32 cookie; - u32 is_cached; /**< [in,out] caching of CPU mappings */ -} _ump_uk_map_mem_s; - -typedef struct _ump_uk_unmap_mem_s -{ - void *ctx; /**< [in,out] user-kernel context (trashed on output) */ - void *mapping; - u32 size; - void *_ukk_private; - u32 cookie; -} _ump_uk_unmap_mem_s; - -typedef struct _ump_uk_msync_s -{ - void *ctx; /**< [in,out] user-kernel context (trashed on output) */ - void *mapping; /**< [in] mapping addr */ - void *address; /**< [in] flush start addr */ - u32 size; /**< [in] size to flush */ - ump_uk_msync_op op; /**< [in] flush operation */ - u32 cookie; /**< [in] cookie stored with reference to the kernel mapping internals */ - u32 secure_id; /**< [in] secure_id that identifies the ump buffer */ - u32 is_cached; /**< [out] caching of CPU mappings */ -} _ump_uk_msync_s; - -typedef struct _ump_uk_cache_operations_control_s -{ - void *ctx; /**< [in,out] user-kernel context (trashed on output) */ - ump_uk_cache_op_control op; /**< [in] cache operations start/stop */ -} _ump_uk_cache_operations_control_s; - - -typedef struct _ump_uk_switch_hw_usage_s -{ - void *ctx; /**< [in,out] user-kernel context (trashed on output) */ - u32 secure_id; /**< [in] secure_id that identifies the ump buffer */ - ump_uk_user new_user; /**< [in] cookie stored with reference to the kernel mapping internals */ - -} _ump_uk_switch_hw_usage_s; - -typedef struct _ump_uk_lock_s -{ - void *ctx; /**< [in,out] user-kernel context (trashed on output) */ - u32 secure_id; /**< [in] secure_id that identifies the ump buffer */ - ump_uk_lock_usage lock_usage; -} _ump_uk_lock_s; - -typedef struct _ump_uk_unlock_s -{ - void *ctx; /**< [in,out] user-kernel context (trashed on output) */ - u32 secure_id; /**< [in] secure_id that identifies the ump buffer */ -} _ump_uk_unlock_s; - -#ifdef __cplusplus -} -#endif - -#endif /* __UMP_UK_TYPES_H__ */ diff --git a/drivers/gpu/arm/ump/common/ump_ukk.h b/drivers/gpu/arm/ump/common/ump_ukk.h deleted file mode 100644 index 568a428d3c0f69..00000000000000 --- a/drivers/gpu/arm/ump/common/ump_ukk.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2010-2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** - * @file ump_ukk.h - * Defines the kernel-side interface of the user-kernel interface - */ - -#ifndef __UMP_UKK_H__ -#define __UMP_UKK_H__ - -#include "mali_osk.h" -#include "ump_uk_types.h" - - -#ifdef __cplusplus -extern "C" { -#endif - - -_mali_osk_errcode_t _ump_ukk_open( void** context ); - -_mali_osk_errcode_t _ump_ukk_close( void** context ); - -_mali_osk_errcode_t _ump_ukk_allocate( _ump_uk_allocate_s *user_interaction ); - -_mali_osk_errcode_t _ump_ukk_release( _ump_uk_release_s *release_info ); - -_mali_osk_errcode_t _ump_ukk_size_get( _ump_uk_size_get_s *user_interaction ); - -_mali_osk_errcode_t _ump_ukk_map_mem( _ump_uk_map_mem_s *args ); - -_mali_osk_errcode_t _ump_uku_get_api_version( _ump_uk_api_version_s *args ); - -void _ump_ukk_unmap_mem( _ump_uk_unmap_mem_s *args ); - -void _ump_ukk_msync( _ump_uk_msync_s *args ); - -void _ump_ukk_cache_operations_control(_ump_uk_cache_operations_control_s* args); - -void _ump_ukk_switch_hw_usage(_ump_uk_switch_hw_usage_s *args ); - -void _ump_ukk_lock(_ump_uk_lock_s *args ); - -void _ump_ukk_unlock(_ump_uk_unlock_s *args ); - -u32 _ump_ukk_report_memory_usage( void ); - -#ifdef __cplusplus -} -#endif - -#endif /* __UMP_UKK_H__ */ diff --git a/drivers/gpu/arm/ump/linux/license/gpl/ump_kernel_license.h b/drivers/gpu/arm/ump/linux/license/gpl/ump_kernel_license.h deleted file mode 100644 index 4db45388c26b2c..00000000000000 --- a/drivers/gpu/arm/ump/linux/license/gpl/ump_kernel_license.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2010, 2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** - * @file ump_kernel_license.h - * Defines for the macro MODULE_LICENSE. - */ - -#ifndef __UMP_KERNEL_LICENSE_H__ -#define __UMP_KERNEL_LICENSE_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -#define UMP_KERNEL_LINUX_LICENSE "GPL" -#define UMP_LICENSE_IS_GPL 1 - -#ifdef __cplusplus -} -#endif - -#endif /* __UMP_KERNEL_LICENSE_H__ */ diff --git a/drivers/gpu/arm/ump/linux/ump_ioctl.h b/drivers/gpu/arm/ump/linux/ump_ioctl.h deleted file mode 100644 index 51787f63515b0d..00000000000000 --- a/drivers/gpu/arm/ump/linux/ump_ioctl.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2010-2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef __UMP_IOCTL_H__ -#define __UMP_IOCTL_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -#include - -#ifndef __user -#define __user -#endif - - -/** - * @file UMP_ioctl.h - * This file describes the interface needed to use the Linux device driver. - * The interface is used by the userpace UMP driver. - */ - -#define UMP_IOCTL_NR 0x90 - - -#define UMP_IOC_QUERY_API_VERSION _IOR(UMP_IOCTL_NR, _UMP_IOC_QUERY_API_VERSION, _ump_uk_api_version_s) -#define UMP_IOC_ALLOCATE _IOWR(UMP_IOCTL_NR, _UMP_IOC_ALLOCATE, _ump_uk_allocate_s) -#define UMP_IOC_RELEASE _IOR(UMP_IOCTL_NR, _UMP_IOC_RELEASE, _ump_uk_release_s) -#define UMP_IOC_SIZE_GET _IOWR(UMP_IOCTL_NR, _UMP_IOC_SIZE_GET, _ump_uk_size_get_s) -#define UMP_IOC_MSYNC _IOW(UMP_IOCTL_NR, _UMP_IOC_MSYNC, _ump_uk_msync_s) - -#define UMP_IOC_CACHE_OPERATIONS_CONTROL _IOW(UMP_IOCTL_NR, _UMP_IOC_CACHE_OPERATIONS_CONTROL, _ump_uk_cache_operations_control_s) -#define UMP_IOC_SWITCH_HW_USAGE _IOW(UMP_IOCTL_NR, _UMP_IOC_SWITCH_HW_USAGE, _ump_uk_switch_hw_usage_s) -#define UMP_IOC_LOCK _IOW(UMP_IOCTL_NR, _UMP_IOC_LOCK, _ump_uk_lock_s) -#define UMP_IOC_UNLOCK _IOW(UMP_IOCTL_NR, _UMP_IOC_UNLOCK, _ump_uk_unlock_s) - - -#ifdef __cplusplus -} -#endif - -#endif /* __UMP_IOCTL_H__ */ diff --git a/drivers/gpu/arm/ump/linux/ump_kernel_linux.c b/drivers/gpu/arm/ump/linux/ump_kernel_linux.c deleted file mode 100644 index d75fba55995bd8..00000000000000 --- a/drivers/gpu/arm/ump/linux/ump_kernel_linux.c +++ /dev/null @@ -1,424 +0,0 @@ -/* - * Copyright (C) 2010-2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include /* kernel module definitions */ -#include /* file system operations */ -#include /* character device definitions */ -#include /* request_mem_region */ -#include /* memory management functions and types */ -#include /* user space access */ -#include -#include -#include - -#include "arch/config.h" /* Configuration for current platform. The symlinc for arch is set by Makefile */ -#include "ump_ioctl.h" -#include "ump_kernel_common.h" -#include -#include -#include "ump_kernel_descriptor_mapping.h" -#include "ump_kernel_memory_backend.h" -#include "ump_kernel_memory_backend_os.h" -#include "ump_kernel_memory_backend_dedicated.h" -#include "ump_kernel_license.h" - -#include "ump_osk.h" -#include "ump_ukk.h" -#include "ump_uk_types.h" -#include "ump_ukk_wrappers.h" -#include "ump_ukk_ref_wrappers.h" - - -/* Module parameter to control log level */ -int ump_debug_level = 2; -module_param(ump_debug_level, int, S_IRUSR | S_IWUSR | S_IWGRP | S_IRGRP | S_IROTH); /* rw-rw-r-- */ -MODULE_PARM_DESC(ump_debug_level, "Higher number, more dmesg output"); - -/* By default the module uses any available major, but it's possible to set it at load time to a specific number */ -int ump_major = 0; -module_param(ump_major, int, S_IRUGO); /* r--r--r-- */ -MODULE_PARM_DESC(ump_major, "Device major number"); - -/* Name of the UMP device driver */ -static char ump_dev_name[] = "ump"; /* should be const, but the functions we call requires non-cost */ - - -#if UMP_LICENSE_IS_GPL -static struct dentry *ump_debugfs_dir = NULL; -#endif - -/* - * The data which we attached to each virtual memory mapping request we get. - * Each memory mapping has a reference to the UMP memory it maps. - * We release this reference when the last memory mapping is unmapped. - */ -typedef struct ump_vma_usage_tracker { - int references; - ump_dd_handle handle; -} ump_vma_usage_tracker; - -struct ump_device { - struct cdev cdev; -#if UMP_LICENSE_IS_GPL - struct class * ump_class; -#endif -}; - -/* The global variable containing the global device data */ -static struct ump_device ump_device; - - -/* Forward declare static functions */ -static int ump_file_open(struct inode *inode, struct file *filp); -static int ump_file_release(struct inode *inode, struct file *filp); -#ifdef HAVE_UNLOCKED_IOCTL -static long ump_file_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); -#else -static int ump_file_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); -#endif -static int ump_file_mmap(struct file * filp, struct vm_area_struct * vma); - - -/* This variable defines the file operations this UMP device driver offer */ -static struct file_operations ump_fops = { - .owner = THIS_MODULE, - .open = ump_file_open, - .release = ump_file_release, -#ifdef HAVE_UNLOCKED_IOCTL - .unlocked_ioctl = ump_file_ioctl, -#else - .ioctl = ump_file_ioctl, -#endif - .mmap = ump_file_mmap -}; - - -/* This function is called by Linux to initialize this module. - * All we do is initialize the UMP device driver. - */ -static int ump_initialize_module(void) -{ - _mali_osk_errcode_t err; - - DBG_MSG(2, ("Inserting UMP device driver. Compiled: %s, time: %s\n", __DATE__, __TIME__)); - - err = ump_kernel_constructor(); - if (_MALI_OSK_ERR_OK != err) { - MSG_ERR(("UMP device driver init failed\n")); - return map_errcode(err); - } - - MSG(("UMP device driver %s loaded\n", SVN_REV_STRING)); - return 0; -} - - - -/* - * This function is called by Linux to unload/terminate/exit/cleanup this module. - * All we do is terminate the UMP device driver. - */ -static void ump_cleanup_module(void) -{ - DBG_MSG(2, ("Unloading UMP device driver\n")); - ump_kernel_destructor(); - DBG_MSG(2, ("Module unloaded\n")); -} - - - -static ssize_t ump_memory_used_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) -{ - char buf[64]; - size_t r; - u32 mem = _ump_ukk_report_memory_usage(); - - r = snprintf(buf, 64, "%u\n", mem); - return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); -} - -static const struct file_operations ump_memory_usage_fops = { - .owner = THIS_MODULE, - .read = ump_memory_used_read, -}; - -/* - * Initialize the UMP device driver. - */ -int ump_kernel_device_initialize(void) -{ - int err; - dev_t dev = 0; -#if UMP_LICENSE_IS_GPL - ump_debugfs_dir = debugfs_create_dir(ump_dev_name, NULL); - if (ERR_PTR(-ENODEV) == ump_debugfs_dir) { - ump_debugfs_dir = NULL; - } else { - debugfs_create_file("memory_usage", 0400, ump_debugfs_dir, NULL, &ump_memory_usage_fops); - } -#endif - - if (0 == ump_major) { - /* auto select a major */ - err = alloc_chrdev_region(&dev, 0, 1, ump_dev_name); - ump_major = MAJOR(dev); - } else { - /* use load time defined major number */ - dev = MKDEV(ump_major, 0); - err = register_chrdev_region(dev, 1, ump_dev_name); - } - - if (0 == err) { - memset(&ump_device, 0, sizeof(ump_device)); - - /* initialize our char dev data */ - cdev_init(&ump_device.cdev, &ump_fops); - ump_device.cdev.owner = THIS_MODULE; - ump_device.cdev.ops = &ump_fops; - - /* register char dev with the kernel */ - err = cdev_add(&ump_device.cdev, dev, 1/*count*/); - if (0 == err) { - -#if UMP_LICENSE_IS_GPL - ump_device.ump_class = class_create(THIS_MODULE, ump_dev_name); - if (IS_ERR(ump_device.ump_class)) { - err = PTR_ERR(ump_device.ump_class); - } else { - struct device * mdev; - mdev = device_create(ump_device.ump_class, NULL, dev, NULL, ump_dev_name); - if (!IS_ERR(mdev)) { - return 0; - } - - err = PTR_ERR(mdev); - } - cdev_del(&ump_device.cdev); -#else - return 0; -#endif - } - - unregister_chrdev_region(dev, 1); - } - - return err; -} - - - -/* - * Terminate the UMP device driver - */ -void ump_kernel_device_terminate(void) -{ - dev_t dev = MKDEV(ump_major, 0); - -#if UMP_LICENSE_IS_GPL - device_destroy(ump_device.ump_class, dev); - class_destroy(ump_device.ump_class); -#endif - - /* unregister char device */ - cdev_del(&ump_device.cdev); - - /* free major */ - unregister_chrdev_region(dev, 1); - -#if UMP_LICENSE_IS_GPL - if(ump_debugfs_dir) - debugfs_remove_recursive(ump_debugfs_dir); -#endif -} - -/* - * Open a new session. User space has called open() on us. - */ -static int ump_file_open(struct inode *inode, struct file *filp) -{ - struct ump_session_data * session_data; - _mali_osk_errcode_t err; - - /* input validation */ - if (0 != MINOR(inode->i_rdev)) { - MSG_ERR(("Minor not zero in ump_file_open()\n")); - return -ENODEV; - } - - /* Call the OS-Independent UMP Open function */ - err = _ump_ukk_open((void**) &session_data ); - if( _MALI_OSK_ERR_OK != err ) { - MSG_ERR(("Ump failed to open a new session\n")); - return map_errcode( err ); - } - - filp->private_data = (void*)session_data; - filp->f_pos = 0; - - return 0; /* success */ -} - - - -/* - * Close a session. User space has called close() or crashed/terminated. - */ -static int ump_file_release(struct inode *inode, struct file *filp) -{ - _mali_osk_errcode_t err; - - err = _ump_ukk_close((void**) &filp->private_data ); - if( _MALI_OSK_ERR_OK != err ) { - return map_errcode( err ); - } - - return 0; /* success */ -} - - - -/* - * Handle IOCTL requests. - */ -#ifdef HAVE_UNLOCKED_IOCTL -static long ump_file_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -#else -static int ump_file_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) -#endif -{ - int err = -ENOTTY; - void __user * argument; - struct ump_session_data * session_data; - -#ifndef HAVE_UNLOCKED_IOCTL - (void)inode; /* inode not used */ -#endif - - session_data = (struct ump_session_data *)filp->private_data; - if (NULL == session_data) { - MSG_ERR(("No session data attached to file object\n")); - return -ENOTTY; - } - - /* interpret the argument as a user pointer to something */ - argument = (void __user *)arg; - - switch (cmd) { - case UMP_IOC_QUERY_API_VERSION: - err = ump_get_api_version_wrapper((u32 __user *)argument, session_data); - break; - - case UMP_IOC_ALLOCATE : - err = ump_allocate_wrapper((u32 __user *)argument, session_data); - break; - - case UMP_IOC_RELEASE: - err = ump_release_wrapper((u32 __user *)argument, session_data); - break; - - case UMP_IOC_SIZE_GET: - err = ump_size_get_wrapper((u32 __user *)argument, session_data); - break; - - case UMP_IOC_MSYNC: - err = ump_msync_wrapper((u32 __user *)argument, session_data); - break; - - case UMP_IOC_CACHE_OPERATIONS_CONTROL: - err = ump_cache_operations_control_wrapper((u32 __user *)argument, session_data); - break; - - case UMP_IOC_SWITCH_HW_USAGE: - err = ump_switch_hw_usage_wrapper((u32 __user *)argument, session_data); - break; - - case UMP_IOC_LOCK: - err = ump_lock_wrapper((u32 __user *)argument, session_data); - break; - - case UMP_IOC_UNLOCK: - err = ump_unlock_wrapper((u32 __user *)argument, session_data); - break; - - default: - DBG_MSG(1, ("No handler for IOCTL. cmd: 0x%08x, arg: 0x%08lx\n", cmd, arg)); - err = -EFAULT; - break; - } - - return err; -} - - -/* - * Handle from OS to map specified virtual memory to specified UMP memory. - */ -static int ump_file_mmap(struct file * filp, struct vm_area_struct * vma) -{ - _ump_uk_map_mem_s args; - _mali_osk_errcode_t err; - struct ump_session_data * session_data; - - /* Validate the session data */ - session_data = (struct ump_session_data *)filp->private_data; - if (NULL == session_data) { - MSG_ERR(("mmap() called without any session data available\n")); - return -EFAULT; - } - - /* Re-pack the arguments that mmap() packed for us */ - args.ctx = session_data; - args.phys_addr = 0; - args.size = vma->vm_end - vma->vm_start; - args._ukk_private = vma; - args.secure_id = vma->vm_pgoff; - args.is_cached = 0; - - if (!(vma->vm_flags & VM_SHARED)) { - args.is_cached = 1; - vma->vm_flags = vma->vm_flags | VM_SHARED | VM_MAYSHARE ; - DBG_MSG(3, ("UMP Map function: Forcing the CPU to use cache\n")); - } - /* By setting this flag, during a process fork; the child process will not have the parent UMP mappings */ - vma->vm_flags |= VM_DONTCOPY; - - DBG_MSG(4, ("UMP vma->flags: %x\n", vma->vm_flags )); - - /* Call the common mmap handler */ - err = _ump_ukk_map_mem( &args ); - if ( _MALI_OSK_ERR_OK != err) { - MSG_ERR(("_ump_ukk_map_mem() failed in function ump_file_mmap()")); - return map_errcode( err ); - } - - return 0; /* success */ -} - -/* Export UMP kernel space API functions */ -EXPORT_SYMBOL(ump_dd_secure_id_get); -EXPORT_SYMBOL(ump_dd_handle_create_from_secure_id); -EXPORT_SYMBOL(ump_dd_phys_block_count_get); -EXPORT_SYMBOL(ump_dd_phys_block_get); -EXPORT_SYMBOL(ump_dd_phys_blocks_get); -EXPORT_SYMBOL(ump_dd_size_get); -EXPORT_SYMBOL(ump_dd_reference_add); -EXPORT_SYMBOL(ump_dd_reference_release); - -/* Export our own extended kernel space allocator */ -EXPORT_SYMBOL(ump_dd_handle_create_from_phys_blocks); - -/* Setup init and exit functions for this module */ -module_init(ump_initialize_module); -module_exit(ump_cleanup_module); - -/* And some module informatio */ -MODULE_LICENSE(UMP_KERNEL_LINUX_LICENSE); -MODULE_AUTHOR("ARM Ltd."); -MODULE_VERSION(SVN_REV_STRING); diff --git a/drivers/gpu/arm/ump/linux/ump_kernel_linux.h b/drivers/gpu/arm/ump/linux/ump_kernel_linux.h deleted file mode 100644 index 7c0a17c55c87e4..00000000000000 --- a/drivers/gpu/arm/ump/linux/ump_kernel_linux.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (C) 2010-2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef __UMP_KERNEL_LINUX_H__ -#define __UMP_KERNEL_LINUX_H__ - -int ump_kernel_device_initialize(void); -void ump_kernel_device_terminate(void); - - -#endif /* __UMP_KERNEL_H__ */ diff --git a/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_dedicated.c b/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_dedicated.c deleted file mode 100644 index ff443293409bf4..00000000000000 --- a/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_dedicated.c +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Copyright (C) 2010-2011, 2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/* needed to detect kernel version specific code */ -#include - -#include - -#include -#include -#include -#include -#include "ump_kernel_common.h" -#include "ump_kernel_memory_backend.h" - - - -#define UMP_BLOCK_SIZE (256UL * 1024UL) /* 256kB, remember to keep the ()s */ - - - -typedef struct block_info { - struct block_info * next; -} block_info; - - - -typedef struct block_allocator { - struct semaphore mutex; - block_info * all_blocks; - block_info * first_free; - u32 base; - u32 num_blocks; - u32 num_free; -} block_allocator; - - -static void block_allocator_shutdown(ump_memory_backend * backend); -static int block_allocator_allocate(void* ctx, ump_dd_mem * mem); -static void block_allocator_release(void * ctx, ump_dd_mem * handle); -static inline u32 get_phys(block_allocator * allocator, block_info * block); -static u32 block_allocator_stat(struct ump_memory_backend *backend); - - - -/* - * Create dedicated memory backend - */ -ump_memory_backend * ump_block_allocator_create(u32 base_address, u32 size) -{ - ump_memory_backend * backend; - block_allocator * allocator; - u32 usable_size; - u32 num_blocks; - - usable_size = (size + UMP_BLOCK_SIZE - 1) & ~(UMP_BLOCK_SIZE - 1); - num_blocks = usable_size / UMP_BLOCK_SIZE; - - if (0 == usable_size) { - DBG_MSG(1, ("Memory block of size %u is unusable\n", size)); - return NULL; - } - - DBG_MSG(5, ("Creating dedicated UMP memory backend. Base address: 0x%08x, size: 0x%08x\n", base_address, size)); - DBG_MSG(6, ("%u usable bytes which becomes %u blocks\n", usable_size, num_blocks)); - - backend = kzalloc(sizeof(ump_memory_backend), GFP_KERNEL); - if (NULL != backend) { - allocator = kmalloc(sizeof(block_allocator), GFP_KERNEL); - if (NULL != allocator) { - allocator->all_blocks = kmalloc(sizeof(block_info) * num_blocks, GFP_KERNEL); - if (NULL != allocator->all_blocks) { - int i; - - allocator->first_free = NULL; - allocator->num_blocks = num_blocks; - allocator->num_free = num_blocks; - allocator->base = base_address; - sema_init(&allocator->mutex, 1); - - for (i = 0; i < num_blocks; i++) { - allocator->all_blocks[i].next = allocator->first_free; - allocator->first_free = &allocator->all_blocks[i]; - } - - backend->ctx = allocator; - backend->allocate = block_allocator_allocate; - backend->release = block_allocator_release; - backend->shutdown = block_allocator_shutdown; - backend->stat = block_allocator_stat; - backend->pre_allocate_physical_check = NULL; - backend->adjust_to_mali_phys = NULL; - - return backend; - } - kfree(allocator); - } - kfree(backend); - } - - return NULL; -} - - - -/* - * Destroy specified dedicated memory backend - */ -static void block_allocator_shutdown(ump_memory_backend * backend) -{ - block_allocator * allocator; - - BUG_ON(!backend); - BUG_ON(!backend->ctx); - - allocator = (block_allocator*)backend->ctx; - - DBG_MSG_IF(1, allocator->num_free != allocator->num_blocks, ("%u blocks still in use during shutdown\n", allocator->num_blocks - allocator->num_free)); - - kfree(allocator->all_blocks); - kfree(allocator); - kfree(backend); -} - - - -static int block_allocator_allocate(void* ctx, ump_dd_mem * mem) -{ - block_allocator * allocator; - u32 left; - block_info * last_allocated = NULL; - int i = 0; - - BUG_ON(!ctx); - BUG_ON(!mem); - - allocator = (block_allocator*)ctx; - left = mem->size_bytes; - - BUG_ON(!left); - BUG_ON(!&allocator->mutex); - - mem->nr_blocks = ((left + UMP_BLOCK_SIZE - 1) & ~(UMP_BLOCK_SIZE - 1)) / UMP_BLOCK_SIZE; - mem->block_array = (ump_dd_physical_block*)vmalloc(sizeof(ump_dd_physical_block) * mem->nr_blocks); - if (NULL == mem->block_array) { - MSG_ERR(("Failed to allocate block array\n")); - return 0; - } - - if (down_interruptible(&allocator->mutex)) { - MSG_ERR(("Could not get mutex to do block_allocate\n")); - return 0; - } - - mem->size_bytes = 0; - - while ((left > 0) && (allocator->first_free)) { - block_info * block; - - block = allocator->first_free; - allocator->first_free = allocator->first_free->next; - block->next = last_allocated; - last_allocated = block; - allocator->num_free--; - - mem->block_array[i].addr = get_phys(allocator, block); - mem->block_array[i].size = UMP_BLOCK_SIZE; - mem->size_bytes += UMP_BLOCK_SIZE; - - i++; - - if (left < UMP_BLOCK_SIZE) left = 0; - else left -= UMP_BLOCK_SIZE; - } - - if (left) { - block_info * block; - /* release all memory back to the pool */ - while (last_allocated) { - block = last_allocated->next; - last_allocated->next = allocator->first_free; - allocator->first_free = last_allocated; - last_allocated = block; - allocator->num_free++; - } - - vfree(mem->block_array); - mem->backend_info = NULL; - mem->block_array = NULL; - - DBG_MSG(4, ("Could not find a mem-block for the allocation.\n")); - up(&allocator->mutex); - - return 0; - } - - mem->backend_info = last_allocated; - - up(&allocator->mutex); - mem->is_cached=0; - - return 1; -} - - - -static void block_allocator_release(void * ctx, ump_dd_mem * handle) -{ - block_allocator * allocator; - block_info * block, * next; - - BUG_ON(!ctx); - BUG_ON(!handle); - - allocator = (block_allocator*)ctx; - block = (block_info*)handle->backend_info; - BUG_ON(!block); - - if (down_interruptible(&allocator->mutex)) { - MSG_ERR(("Allocator release: Failed to get mutex - memory leak\n")); - return; - } - - while (block) { - next = block->next; - - BUG_ON( (block < allocator->all_blocks) || (block > (allocator->all_blocks + allocator->num_blocks))); - - block->next = allocator->first_free; - allocator->first_free = block; - allocator->num_free++; - - block = next; - } - DBG_MSG(3, ("%d blocks free after release call\n", allocator->num_free)); - up(&allocator->mutex); - - vfree(handle->block_array); - handle->block_array = NULL; -} - - - -/* - * Helper function for calculating the physical base adderss of a memory block - */ -static inline u32 get_phys(block_allocator * allocator, block_info * block) -{ - return allocator->base + ((block - allocator->all_blocks) * UMP_BLOCK_SIZE); -} - -static u32 block_allocator_stat(struct ump_memory_backend *backend) -{ - block_allocator *allocator; - BUG_ON(!backend); - allocator = (block_allocator*)backend->ctx; - BUG_ON(!allocator); - - return (allocator->num_blocks - allocator->num_free)* UMP_BLOCK_SIZE; -} diff --git a/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_dedicated.h b/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_dedicated.h deleted file mode 100644 index fa4bdccfe32cd2..00000000000000 --- a/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_dedicated.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2010 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** - * @file ump_kernel_memory_backend_dedicated.h - */ - -#ifndef __UMP_KERNEL_MEMORY_BACKEND_DEDICATED_H__ -#define __UMP_KERNEL_MEMORY_BACKEND_DEDICATED_H__ - -#include "ump_kernel_memory_backend.h" - -ump_memory_backend * ump_block_allocator_create(u32 base_address, u32 size); - -#endif /* __UMP_KERNEL_MEMORY_BACKEND_DEDICATED_H__ */ - diff --git a/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_os.c b/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_os.c deleted file mode 100644 index d3fa08025c2adc..00000000000000 --- a/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_os.c +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright (C) 2010-2011, 2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/* needed to detect kernel version specific code */ -#include - -#include - -#include -#include -#include -#include -#include -#include -#include "ump_kernel_common.h" -#include "ump_kernel_memory_backend.h" - - - -typedef struct os_allocator { - struct semaphore mutex; - u32 num_pages_max; /**< Maximum number of pages to allocate from the OS */ - u32 num_pages_allocated; /**< Number of pages allocated from the OS */ -} os_allocator; - - - -static void os_free(void* ctx, ump_dd_mem * descriptor); -static int os_allocate(void* ctx, ump_dd_mem * descriptor); -static void os_memory_backend_destroy(ump_memory_backend * backend); -static u32 os_stat(struct ump_memory_backend *backend); - - - -/* - * Create OS memory backend - */ -ump_memory_backend * ump_os_memory_backend_create(const int max_allocation) -{ - ump_memory_backend * backend; - os_allocator * info; - - info = kmalloc(sizeof(os_allocator), GFP_KERNEL); - if (NULL == info) { - return NULL; - } - - info->num_pages_max = max_allocation >> PAGE_SHIFT; - info->num_pages_allocated = 0; - - sema_init(&info->mutex, 1); - - backend = kmalloc(sizeof(ump_memory_backend), GFP_KERNEL); - if (NULL == backend) { - kfree(info); - return NULL; - } - - backend->ctx = info; - backend->allocate = os_allocate; - backend->release = os_free; - backend->shutdown = os_memory_backend_destroy; - backend->stat = os_stat; - backend->pre_allocate_physical_check = NULL; - backend->adjust_to_mali_phys = NULL; - - return backend; -} - - - -/* - * Destroy specified OS memory backend - */ -static void os_memory_backend_destroy(ump_memory_backend * backend) -{ - os_allocator * info = (os_allocator*)backend->ctx; - - DBG_MSG_IF(1, 0 != info->num_pages_allocated, ("%d pages still in use during shutdown\n", info->num_pages_allocated)); - - kfree(info); - kfree(backend); -} - - - -/* - * Allocate UMP memory - */ -static int os_allocate(void* ctx, ump_dd_mem * descriptor) -{ - u32 left; - os_allocator * info; - int pages_allocated = 0; - int is_cached; - - BUG_ON(!descriptor); - BUG_ON(!ctx); - - info = (os_allocator*)ctx; - left = descriptor->size_bytes; - is_cached = descriptor->is_cached; - - if (down_interruptible(&info->mutex)) { - DBG_MSG(1, ("Failed to get mutex in os_free\n")); - return 0; /* failure */ - } - - descriptor->backend_info = NULL; - descriptor->nr_blocks = ((left + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) >> PAGE_SHIFT; - - DBG_MSG(5, ("Allocating page array. Size: %lu\n", descriptor->nr_blocks * sizeof(ump_dd_physical_block))); - - descriptor->block_array = (ump_dd_physical_block *)vmalloc(sizeof(ump_dd_physical_block) * descriptor->nr_blocks); - if (NULL == descriptor->block_array) { - up(&info->mutex); - DBG_MSG(1, ("Block array could not be allocated\n")); - return 0; /* failure */ - } - - while (left > 0 && ((info->num_pages_allocated + pages_allocated) < info->num_pages_max)) { - struct page * new_page; - - if (is_cached) { - new_page = alloc_page(GFP_HIGHUSER | __GFP_ZERO | __GFP_REPEAT | __GFP_NOWARN); - } else { - new_page = alloc_page(GFP_HIGHUSER | __GFP_ZERO | __GFP_REPEAT | __GFP_NOWARN | __GFP_COLD); - } - if (NULL == new_page) { - break; - } - - /* Ensure page caches are flushed. */ - if ( is_cached ) { - descriptor->block_array[pages_allocated].addr = page_to_phys(new_page); - descriptor->block_array[pages_allocated].size = PAGE_SIZE; - } else { - descriptor->block_array[pages_allocated].addr = dma_map_page(NULL, new_page, 0, PAGE_SIZE, DMA_BIDIRECTIONAL ); - descriptor->block_array[pages_allocated].size = PAGE_SIZE; - } - - DBG_MSG(5, ("Allocated page 0x%08lx cached: %d\n", descriptor->block_array[pages_allocated].addr, is_cached)); - - if (left < PAGE_SIZE) { - left = 0; - } else { - left -= PAGE_SIZE; - } - - pages_allocated++; - } - - DBG_MSG(5, ("Alloce for ID:%2d got %d pages, cached: %d\n", descriptor->secure_id, pages_allocated)); - - if (left) { - DBG_MSG(1, ("Failed to allocate needed pages\n")); - - while(pages_allocated) { - pages_allocated--; - if ( !is_cached ) { - dma_unmap_page(NULL, descriptor->block_array[pages_allocated].addr, PAGE_SIZE, DMA_BIDIRECTIONAL); - } - __free_page(pfn_to_page(descriptor->block_array[pages_allocated].addr >> PAGE_SHIFT) ); - } - - up(&info->mutex); - - return 0; /* failure */ - } - - info->num_pages_allocated += pages_allocated; - - DBG_MSG(6, ("%d out of %d pages now allocated\n", info->num_pages_allocated, info->num_pages_max)); - - up(&info->mutex); - - return 1; /* success*/ -} - - -/* - * Free specified UMP memory - */ -static void os_free(void* ctx, ump_dd_mem * descriptor) -{ - os_allocator * info; - int i; - - BUG_ON(!ctx); - BUG_ON(!descriptor); - - info = (os_allocator*)ctx; - - BUG_ON(descriptor->nr_blocks > info->num_pages_allocated); - - if (down_interruptible(&info->mutex)) { - DBG_MSG(1, ("Failed to get mutex in os_free\n")); - return; - } - - DBG_MSG(5, ("Releasing %lu OS pages\n", descriptor->nr_blocks)); - - info->num_pages_allocated -= descriptor->nr_blocks; - - up(&info->mutex); - - for ( i = 0; i < descriptor->nr_blocks; i++) { - DBG_MSG(6, ("Freeing physical page. Address: 0x%08lx\n", descriptor->block_array[i].addr)); - if ( ! descriptor->is_cached) { - dma_unmap_page(NULL, descriptor->block_array[i].addr, PAGE_SIZE, DMA_BIDIRECTIONAL); - } - __free_page(pfn_to_page(descriptor->block_array[i].addr>>PAGE_SHIFT) ); - } - - vfree(descriptor->block_array); -} - - -static u32 os_stat(struct ump_memory_backend *backend) -{ - os_allocator *info; - info = (os_allocator*)backend->ctx; - return info->num_pages_allocated * _MALI_OSK_MALI_PAGE_SIZE; -} diff --git a/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_os.h b/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_os.h deleted file mode 100644 index f924705cf742fd..00000000000000 --- a/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_os.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2010 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** - * @file ump_kernel_memory_backend_os.h - */ - -#ifndef __UMP_KERNEL_MEMORY_BACKEND_OS_H__ -#define __UMP_KERNEL_MEMORY_BACKEND_OS_H__ - -#include "ump_kernel_memory_backend.h" - -ump_memory_backend * ump_os_memory_backend_create(const int max_allocation); - -#endif /* __UMP_KERNEL_MEMORY_BACKEND_OS_H__ */ - diff --git a/drivers/gpu/arm/ump/linux/ump_memory_backend.c b/drivers/gpu/arm/ump/linux/ump_memory_backend.c deleted file mode 100644 index e7f84dbbcf1daa..00000000000000 --- a/drivers/gpu/arm/ump/linux/ump_memory_backend.c +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2010, 2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include /* kernel module definitions */ -#include /* request_mem_region */ - -#include "arch/config.h" /* Configuration for current platform. The symlink for arch is set by Makefile */ - -#include "ump_osk.h" -#include "ump_kernel_common.h" -#include "ump_kernel_memory_backend_os.h" -#include "ump_kernel_memory_backend_dedicated.h" - -/* Configure which dynamic memory allocator to use */ -int ump_backend = ARCH_UMP_BACKEND_DEFAULT; -module_param(ump_backend, int, S_IRUGO); /* r--r--r-- */ -MODULE_PARM_DESC(ump_backend, "0 = dedicated memory backend (default), 1 = OS memory backend"); - -/* The base address of the memory block for the dedicated memory backend */ -unsigned int ump_memory_address = ARCH_UMP_MEMORY_ADDRESS_DEFAULT; -module_param(ump_memory_address, uint, S_IRUGO); /* r--r--r-- */ -MODULE_PARM_DESC(ump_memory_address, "The physical address to map for the dedicated memory backend"); - -/* The size of the memory block for the dedicated memory backend */ -unsigned int ump_memory_size = ARCH_UMP_MEMORY_SIZE_DEFAULT; -module_param(ump_memory_size, uint, S_IRUGO); /* r--r--r-- */ -MODULE_PARM_DESC(ump_memory_size, "The size of fixed memory to map in the dedicated memory backend"); - -ump_memory_backend* ump_memory_backend_create ( void ) -{ - ump_memory_backend * backend = NULL; - - /* Create the dynamic memory allocator backend */ - if (0 == ump_backend) { - DBG_MSG(2, ("Using dedicated memory backend\n")); - - DBG_MSG(2, ("Requesting dedicated memory: 0x%08x, size: %u\n", ump_memory_address, ump_memory_size)); - /* Ask the OS if we can use the specified physical memory */ - if (NULL == request_mem_region(ump_memory_address, ump_memory_size, "UMP Memory")) { - MSG_ERR(("Failed to request memory region (0x%08X - 0x%08X). Is Mali DD already loaded?\n", ump_memory_address, ump_memory_address + ump_memory_size - 1)); - return NULL; - } - backend = ump_block_allocator_create(ump_memory_address, ump_memory_size); - } else if (1 == ump_backend) { - DBG_MSG(2, ("Using OS memory backend, allocation limit: %d\n", ump_memory_size)); - backend = ump_os_memory_backend_create(ump_memory_size); - } - - return backend; -} - -void ump_memory_backend_destroy( void ) -{ - if (0 == ump_backend) { - DBG_MSG(2, ("Releasing dedicated memory: 0x%08x\n", ump_memory_address)); - release_mem_region(ump_memory_address, ump_memory_size); - } -} diff --git a/drivers/gpu/arm/ump/linux/ump_osk_atomics.c b/drivers/gpu/arm/ump/linux/ump_osk_atomics.c deleted file mode 100644 index ef1902e5391b2b..00000000000000 --- a/drivers/gpu/arm/ump/linux/ump_osk_atomics.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2010 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** - * @file ump_osk_atomics.c - * Implementation of the OS abstraction layer for the UMP kernel device driver - */ - -#include "ump_osk.h" -#include - -int _ump_osk_atomic_dec_and_read( _mali_osk_atomic_t *atom ) -{ - return atomic_dec_return((atomic_t *)&atom->u.val); -} - -int _ump_osk_atomic_inc_and_read( _mali_osk_atomic_t *atom ) -{ - return atomic_inc_return((atomic_t *)&atom->u.val); -} diff --git a/drivers/gpu/arm/ump/linux/ump_osk_low_level_mem.c b/drivers/gpu/arm/ump/linux/ump_osk_low_level_mem.c deleted file mode 100644 index f19858382e6381..00000000000000 --- a/drivers/gpu/arm/ump/linux/ump_osk_low_level_mem.c +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright (C) 2010-2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** - * @file ump_osk_memory.c - * Implementation of the OS abstraction layer for the kernel device driver - */ - -/* needed to detect kernel version specific code */ -#include - -#include "ump_osk.h" -#include "ump_uk_types.h" -#include "ump_ukk.h" -#include "ump_kernel_common.h" -#include /* kernel module definitions */ -#include -#include -#include - -#include -#include /* to verify pointers from user space */ -#include -#include - -typedef struct ump_vma_usage_tracker { - atomic_t references; - ump_memory_allocation *descriptor; -} ump_vma_usage_tracker; - -static void ump_vma_open(struct vm_area_struct * vma); -static void ump_vma_close(struct vm_area_struct * vma); -static int ump_cpu_page_fault_handler(struct vm_area_struct *vma, struct vm_fault *vmf); - -static struct vm_operations_struct ump_vm_ops = { - .open = ump_vma_open, - .close = ump_vma_close, - .fault = ump_cpu_page_fault_handler -}; - -/* - * Page fault for VMA region - * This should never happen since we always map in the entire virtual memory range. - */ -static int ump_cpu_page_fault_handler(struct vm_area_struct *vma, struct vm_fault *vmf) -{ - void __user * address; - address = vmf->virtual_address; - - MSG_ERR(("Page-fault in UMP memory region caused by the CPU\n")); - MSG_ERR(("VMA: 0x%08lx, virtual address: 0x%08lx\n", (unsigned long)vma, address)); - - return VM_FAULT_SIGBUS; -} - -static void ump_vma_open(struct vm_area_struct * vma) -{ - ump_vma_usage_tracker * vma_usage_tracker; - int new_val; - - vma_usage_tracker = (ump_vma_usage_tracker*)vma->vm_private_data; - BUG_ON(NULL == vma_usage_tracker); - - new_val = atomic_inc_return(&vma_usage_tracker->references); - - DBG_MSG(4, ("VMA open, VMA reference count incremented. VMA: 0x%08lx, reference count: %d\n", (unsigned long)vma, new_val)); -} - -static void ump_vma_close(struct vm_area_struct * vma) -{ - ump_vma_usage_tracker * vma_usage_tracker; - _ump_uk_unmap_mem_s args; - int new_val; - - vma_usage_tracker = (ump_vma_usage_tracker*)vma->vm_private_data; - BUG_ON(NULL == vma_usage_tracker); - - new_val = atomic_dec_return(&vma_usage_tracker->references); - - DBG_MSG(4, ("VMA close, VMA reference count decremented. VMA: 0x%08lx, reference count: %d\n", (unsigned long)vma, new_val)); - - if (0 == new_val) { - ump_memory_allocation * descriptor; - - descriptor = vma_usage_tracker->descriptor; - - args.ctx = descriptor->ump_session; - args.cookie = descriptor->cookie; - args.mapping = descriptor->mapping; - args.size = descriptor->size; - - args._ukk_private = NULL; /** @note unused */ - - DBG_MSG(4, ("No more VMA references left, releasing UMP memory\n")); - _ump_ukk_unmap_mem( & args ); - - /* vma_usage_tracker is free()d by _ump_osk_mem_mapregion_term() */ - } -} - -_mali_osk_errcode_t _ump_osk_mem_mapregion_init( ump_memory_allocation * descriptor ) -{ - ump_vma_usage_tracker * vma_usage_tracker; - struct vm_area_struct *vma; - - if (NULL == descriptor) return _MALI_OSK_ERR_FAULT; - - vma_usage_tracker = kmalloc(sizeof(ump_vma_usage_tracker), GFP_KERNEL); - if (NULL == vma_usage_tracker) { - DBG_MSG(1, ("Failed to allocate memory for ump_vma_usage_tracker in _mali_osk_mem_mapregion_init\n")); - return -_MALI_OSK_ERR_FAULT; - } - - vma = (struct vm_area_struct*)descriptor->process_mapping_info; - if (NULL == vma ) { - kfree(vma_usage_tracker); - return _MALI_OSK_ERR_FAULT; - } - - vma->vm_private_data = vma_usage_tracker; - vma->vm_flags |= VM_IO; - vma->vm_flags |= VM_DONTDUMP; - vma->vm_flags |= VM_DONTEXPAND; - vma->vm_flags |= VM_PFNMAP; - - if (0==descriptor->is_cached) { - vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); - } - DBG_MSG(3, ("Mapping with page_prot: 0x%x\n", vma->vm_page_prot )); - - /* Setup the functions which handle further VMA handling */ - vma->vm_ops = &ump_vm_ops; - - /* Do the va range allocation - in this case, it was done earlier, so we copy in that information */ - descriptor->mapping = (void __user*)vma->vm_start; - - atomic_set(&vma_usage_tracker->references, 1); /*this can later be increased if process is forked, see ump_vma_open() */ - vma_usage_tracker->descriptor = descriptor; - - return _MALI_OSK_ERR_OK; -} - -void _ump_osk_mem_mapregion_term( ump_memory_allocation * descriptor ) -{ - struct vm_area_struct* vma; - ump_vma_usage_tracker * vma_usage_tracker; - - if (NULL == descriptor) return; - - /* Linux does the right thing as part of munmap to remove the mapping - * All that remains is that we remove the vma_usage_tracker setup in init() */ - vma = (struct vm_area_struct*)descriptor->process_mapping_info; - - vma_usage_tracker = vma->vm_private_data; - - /* We only get called if mem_mapregion_init succeeded */ - kfree(vma_usage_tracker); - return; -} - -_mali_osk_errcode_t _ump_osk_mem_mapregion_map( ump_memory_allocation * descriptor, u32 offset, u32 * phys_addr, unsigned long size ) -{ - struct vm_area_struct *vma; - _mali_osk_errcode_t retval; - - if (NULL == descriptor) return _MALI_OSK_ERR_FAULT; - - vma = (struct vm_area_struct*)descriptor->process_mapping_info; - - if (NULL == vma ) return _MALI_OSK_ERR_FAULT; - - retval = remap_pfn_range( vma, ((u32)descriptor->mapping) + offset, (*phys_addr) >> PAGE_SHIFT, size, vma->vm_page_prot) ? _MALI_OSK_ERR_FAULT : _MALI_OSK_ERR_OK;; - - DBG_MSG(4, ("Mapping virtual to physical memory. ID: %u, vma: 0x%08lx, virtual addr:0x%08lx, physical addr: 0x%08lx, size:%lu, prot:0x%x, vm_flags:0x%x RETVAL: 0x%x\n", - ump_dd_secure_id_get(descriptor->handle), - (unsigned long)vma, - (unsigned long)(vma->vm_start + offset), - (unsigned long)*phys_addr, - size, - (unsigned int)vma->vm_page_prot, vma->vm_flags, retval)); - - return retval; -} - -static void level1_cache_flush_all(void) -{ - DBG_MSG(4, ("UMP[xx] Flushing complete L1 cache\n")); - __cpuc_flush_kern_all(); -} - -void _ump_osk_msync( ump_dd_mem * mem, void * virt, u32 offset, u32 size, ump_uk_msync_op op, ump_session_data * session_data ) -{ - int i; - - /* Flush L1 using virtual address, the entire range in one go. - * Only flush if user space process has a valid write mapping on given address. */ - if( (mem) && (virt!=NULL) && (access_ok(VERIFY_WRITE, virt, size)) ) { - __cpuc_flush_dcache_area(virt, size); - DBG_MSG(3, ("UMP[%02u] Flushing CPU L1 Cache. CPU address: %x, size: %x\n", mem->secure_id, virt, size)); - } else { - if (session_data) { - if (op == _UMP_UK_MSYNC_FLUSH_L1 ) { - DBG_MSG(4, ("UMP Pending L1 cache flushes: %d\n", session_data->has_pending_level1_cache_flush)); - session_data->has_pending_level1_cache_flush = 0; - level1_cache_flush_all(); - return; - } else { - if (session_data->cache_operations_ongoing) { - session_data->has_pending_level1_cache_flush++; - DBG_MSG(4, ("UMP[%02u] Defering the L1 flush. Nr pending:%d\n", mem->secure_id, session_data->has_pending_level1_cache_flush) ); - } else { - /* Flushing the L1 cache for each switch_user() if ump_cache_operations_control(START) is not called */ - level1_cache_flush_all(); - } - } - } else { - DBG_MSG(4, ("Unkown state %s %d\n", __FUNCTION__, __LINE__)); - level1_cache_flush_all(); - } - } - - if ( NULL == mem ) return; - - if ( mem->size_bytes==size) { - DBG_MSG(3, ("UMP[%02u] Flushing CPU L2 Cache\n",mem->secure_id)); - } else { - DBG_MSG(3, ("UMP[%02u] Flushing CPU L2 Cache. Blocks:%u, TotalSize:%u. FlushSize:%u Offset:0x%x FirstPaddr:0x%08x\n", - mem->secure_id, mem->nr_blocks, mem->size_bytes, size, offset, mem->block_array[0].addr)); - } - - - /* Flush L2 using physical addresses, block for block. */ - for (i=0 ; i < mem->nr_blocks; i++) { - u32 start_p, end_p; - ump_dd_physical_block *block; - block = &mem->block_array[i]; - - if(offset >= block->size) { - offset -= block->size; - continue; - } - - if(offset) { - start_p = (u32)block->addr + offset; - /* We'll zero the offset later, after using it to calculate end_p. */ - } else { - start_p = (u32)block->addr; - } - - if(size < block->size - offset) { - end_p = start_p + size - 1; - size = 0; - } else { - if(offset) { - end_p = start_p + (block->size - offset - 1); - size -= block->size - offset; - offset = 0; - } else { - end_p = start_p + block->size - 1; - size -= block->size; - } - } - - switch(op) { - case _UMP_UK_MSYNC_CLEAN: - outer_clean_range(start_p, end_p); - break; - case _UMP_UK_MSYNC_CLEAN_AND_INVALIDATE: - outer_flush_range(start_p, end_p); - break; - case _UMP_UK_MSYNC_INVALIDATE: - outer_inv_range(start_p, end_p); - break; - default: - break; - } - - if(0 == size) { - /* Nothing left to flush. */ - break; - } - } - - return; -} diff --git a/drivers/gpu/arm/ump/linux/ump_osk_misc.c b/drivers/gpu/arm/ump/linux/ump_osk_misc.c deleted file mode 100644 index ddbce844cd7d28..00000000000000 --- a/drivers/gpu/arm/ump/linux/ump_osk_misc.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2010, 2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** - * @file ump_osk_misc.c - * Implementation of the OS abstraction layer for the UMP kernel device driver - */ - - -#include "ump_osk.h" - -#include -#include "ump_kernel_linux.h" - -/* is called from ump_kernel_constructor in common code */ -_mali_osk_errcode_t _ump_osk_init( void ) -{ - if (0 != ump_kernel_device_initialize()) { - return _MALI_OSK_ERR_FAULT; - } - - return _MALI_OSK_ERR_OK; -} - -_mali_osk_errcode_t _ump_osk_term( void ) -{ - ump_kernel_device_terminate(); - return _MALI_OSK_ERR_OK; -} diff --git a/drivers/gpu/arm/ump/linux/ump_ukk_ref_wrappers.c b/drivers/gpu/arm/ump/linux/ump_ukk_ref_wrappers.c deleted file mode 100644 index 8cbe538e27b7c9..00000000000000 --- a/drivers/gpu/arm/ump/linux/ump_ukk_ref_wrappers.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2010, 2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** - * @file ump_ukk_wrappers.c - * Defines the wrapper functions which turn Linux IOCTL calls into _ukk_ calls for the reference implementation - */ - - -#include /* user space access */ - -#include "ump_osk.h" -#include "ump_uk_types.h" -#include "ump_ukk.h" -#include "ump_kernel_common.h" - -/* - * IOCTL operation; Allocate UMP memory - */ -int ump_allocate_wrapper(u32 __user * argument, struct ump_session_data * session_data) -{ - _ump_uk_allocate_s user_interaction; - _mali_osk_errcode_t err; - - /* Sanity check input parameters */ - if (NULL == argument || NULL == session_data) { - MSG_ERR(("NULL parameter in ump_ioctl_allocate()\n")); - return -ENOTTY; - } - - /* Copy the user space memory to kernel space (so we safely can read it) */ - if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) { - MSG_ERR(("copy_from_user() in ump_ioctl_allocate()\n")); - return -EFAULT; - } - - user_interaction.ctx = (void *) session_data; - - err = _ump_ukk_allocate( &user_interaction ); - if( _MALI_OSK_ERR_OK != err ) { - DBG_MSG(1, ("_ump_ukk_allocate() failed in ump_ioctl_allocate()\n")); - return map_errcode(err); - } - user_interaction.ctx = NULL; - - if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) { - /* If the copy fails then we should release the memory. We can use the IOCTL release to accomplish this */ - _ump_uk_release_s release_args; - - MSG_ERR(("copy_to_user() failed in ump_ioctl_allocate()\n")); - - release_args.ctx = (void *) session_data; - release_args.secure_id = user_interaction.secure_id; - - err = _ump_ukk_release( &release_args ); - if(_MALI_OSK_ERR_OK != err) { - MSG_ERR(("_ump_ukk_release() also failed when trying to release newly allocated memory in ump_ioctl_allocate()\n")); - } - - return -EFAULT; - } - - return 0; /* success */ -} diff --git a/drivers/gpu/arm/ump/linux/ump_ukk_ref_wrappers.h b/drivers/gpu/arm/ump/linux/ump_ukk_ref_wrappers.h deleted file mode 100644 index 43dabd4d368361..00000000000000 --- a/drivers/gpu/arm/ump/linux/ump_ukk_ref_wrappers.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2010, 2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** - * @file ump_ukk_wrappers.h - * Defines the wrapper functions which turn Linux IOCTL calls into _ukk_ calls for the reference implementation - */ - -#ifndef __UMP_UKK_REF_WRAPPERS_H__ -#define __UMP_UKK_REF_WRAPPERS_H__ - -#include -#include "ump_kernel_common.h" - -#ifdef __cplusplus -extern "C" { -#endif - - -int ump_allocate_wrapper(u32 __user * argument, struct ump_session_data * session_data); - - -#ifdef __cplusplus -} -#endif - -#endif /* __UMP_UKK_REF_WRAPPERS_H__ */ diff --git a/drivers/gpu/arm/ump/linux/ump_ukk_wrappers.c b/drivers/gpu/arm/ump/linux/ump_ukk_wrappers.c deleted file mode 100644 index 6c1831eefe5275..00000000000000 --- a/drivers/gpu/arm/ump/linux/ump_ukk_wrappers.c +++ /dev/null @@ -1,280 +0,0 @@ -/* - * Copyright (C) 2010-2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** - * @file ump_ukk_wrappers.c - * Defines the wrapper functions which turn Linux IOCTL calls into _ukk_ calls - */ - -#include /* user space access */ - -#include "ump_osk.h" -#include "ump_uk_types.h" -#include "ump_ukk.h" -#include "ump_kernel_common.h" - -/* - * IOCTL operation; Negotiate version of IOCTL API - */ -int ump_get_api_version_wrapper(u32 __user * argument, struct ump_session_data * session_data) -{ - _ump_uk_api_version_s version_info; - _mali_osk_errcode_t err; - - /* Sanity check input parameters */ - if (NULL == argument || NULL == session_data) { - MSG_ERR(("NULL parameter in ump_ioctl_get_api_version()\n")); - return -ENOTTY; - } - - /* Copy the user space memory to kernel space (so we safely can read it) */ - if (0 != copy_from_user(&version_info, argument, sizeof(version_info))) { - MSG_ERR(("copy_from_user() in ump_ioctl_get_api_version()\n")); - return -EFAULT; - } - - version_info.ctx = (void*) session_data; - err = _ump_uku_get_api_version( &version_info ); - if( _MALI_OSK_ERR_OK != err ) { - MSG_ERR(("_ump_uku_get_api_version() failed in ump_ioctl_get_api_version()\n")); - return map_errcode(err); - } - - version_info.ctx = NULL; - - /* Copy ouput data back to user space */ - if (0 != copy_to_user(argument, &version_info, sizeof(version_info))) { - MSG_ERR(("copy_to_user() failed in ump_ioctl_get_api_version()\n")); - return -EFAULT; - } - - return 0; /* success */ -} - - -/* - * IOCTL operation; Release reference to specified UMP memory. - */ -int ump_release_wrapper(u32 __user * argument, struct ump_session_data * session_data) -{ - _ump_uk_release_s release_args; - _mali_osk_errcode_t err; - - /* Sanity check input parameters */ - if (NULL == session_data) { - MSG_ERR(("NULL parameter in ump_ioctl_release()\n")); - return -ENOTTY; - } - - /* Copy the user space memory to kernel space (so we safely can read it) */ - if (0 != copy_from_user(&release_args, argument, sizeof(release_args))) { - MSG_ERR(("copy_from_user() in ump_ioctl_get_api_version()\n")); - return -EFAULT; - } - - release_args.ctx = (void*) session_data; - err = _ump_ukk_release( &release_args ); - if( _MALI_OSK_ERR_OK != err ) { - MSG_ERR(("_ump_ukk_release() failed in ump_ioctl_release()\n")); - return map_errcode(err); - } - - - return 0; /* success */ -} - -/* - * IOCTL operation; Return size for specified UMP memory. - */ -int ump_size_get_wrapper(u32 __user * argument, struct ump_session_data * session_data) -{ - _ump_uk_size_get_s user_interaction; - _mali_osk_errcode_t err; - - /* Sanity check input parameters */ - if (NULL == argument || NULL == session_data) { - MSG_ERR(("NULL parameter in ump_ioctl_size_get()\n")); - return -ENOTTY; - } - - if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) { - MSG_ERR(("copy_from_user() in ump_ioctl_size_get()\n")); - return -EFAULT; - } - - user_interaction.ctx = (void *) session_data; - err = _ump_ukk_size_get( &user_interaction ); - if( _MALI_OSK_ERR_OK != err ) { - MSG_ERR(("_ump_ukk_size_get() failed in ump_ioctl_size_get()\n")); - return map_errcode(err); - } - - user_interaction.ctx = NULL; - - if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) { - MSG_ERR(("copy_to_user() failed in ump_ioctl_size_get()\n")); - return -EFAULT; - } - - return 0; /* success */ -} - -/* - * IOCTL operation; Do cache maintenance on specified UMP memory. - */ -int ump_msync_wrapper(u32 __user * argument, struct ump_session_data * session_data) -{ - _ump_uk_msync_s user_interaction; - - /* Sanity check input parameters */ - if (NULL == argument || NULL == session_data) { - MSG_ERR(("NULL parameter in ump_ioctl_size_get()\n")); - return -ENOTTY; - } - - if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) { - MSG_ERR(("copy_from_user() in ump_ioctl_msync()\n")); - return -EFAULT; - } - - user_interaction.ctx = (void *) session_data; - - _ump_ukk_msync( &user_interaction ); - - user_interaction.ctx = NULL; - - if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) { - MSG_ERR(("copy_to_user() failed in ump_ioctl_msync()\n")); - return -EFAULT; - } - - return 0; /* success */ -} -int ump_cache_operations_control_wrapper(u32 __user * argument, struct ump_session_data * session_data) -{ - _ump_uk_cache_operations_control_s user_interaction; - - /* Sanity check input parameters */ - if (NULL == argument || NULL == session_data) { - MSG_ERR(("NULL parameter in ump_ioctl_size_get()\n")); - return -ENOTTY; - } - - if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) { - MSG_ERR(("copy_from_user() in ump_ioctl_cache_operations_control()\n")); - return -EFAULT; - } - - user_interaction.ctx = (void *) session_data; - - _ump_ukk_cache_operations_control((_ump_uk_cache_operations_control_s*) &user_interaction ); - - user_interaction.ctx = NULL; - -#if 0 /* No data to copy back */ - if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) { - MSG_ERR(("copy_to_user() failed in ump_ioctl_cache_operations_control()\n")); - return -EFAULT; - } -#endif - return 0; /* success */ -} - -int ump_switch_hw_usage_wrapper(u32 __user * argument, struct ump_session_data * session_data) -{ - _ump_uk_switch_hw_usage_s user_interaction; - - /* Sanity check input parameters */ - if (NULL == argument || NULL == session_data) { - MSG_ERR(("NULL parameter in ump_ioctl_size_get()\n")); - return -ENOTTY; - } - - if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) { - MSG_ERR(("copy_from_user() in ump_ioctl_switch_hw_usage()\n")); - return -EFAULT; - } - - user_interaction.ctx = (void *) session_data; - - _ump_ukk_switch_hw_usage( &user_interaction ); - - user_interaction.ctx = NULL; - -#if 0 /* No data to copy back */ - if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) { - MSG_ERR(("copy_to_user() failed in ump_ioctl_switch_hw_usage()\n")); - return -EFAULT; - } -#endif - return 0; /* success */ -} - -int ump_lock_wrapper(u32 __user * argument, struct ump_session_data * session_data) -{ - _ump_uk_lock_s user_interaction; - - /* Sanity check input parameters */ - if (NULL == argument || NULL == session_data) { - MSG_ERR(("NULL parameter in ump_ioctl_size_get()\n")); - return -ENOTTY; - } - - if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) { - MSG_ERR(("copy_from_user() in ump_ioctl_switch_hw_usage()\n")); - return -EFAULT; - } - - user_interaction.ctx = (void *) session_data; - - _ump_ukk_lock( &user_interaction ); - - user_interaction.ctx = NULL; - -#if 0 /* No data to copy back */ - if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) { - MSG_ERR(("copy_to_user() failed in ump_ioctl_switch_hw_usage()\n")); - return -EFAULT; - } -#endif - - return 0; /* success */ -} - -int ump_unlock_wrapper(u32 __user * argument, struct ump_session_data * session_data) -{ - _ump_uk_unlock_s user_interaction; - - /* Sanity check input parameters */ - if (NULL == argument || NULL == session_data) { - MSG_ERR(("NULL parameter in ump_ioctl_size_get()\n")); - return -ENOTTY; - } - - if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) { - MSG_ERR(("copy_from_user() in ump_ioctl_switch_hw_usage()\n")); - return -EFAULT; - } - - user_interaction.ctx = (void *) session_data; - - _ump_ukk_unlock( &user_interaction ); - - user_interaction.ctx = NULL; - -#if 0 /* No data to copy back */ - if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) { - MSG_ERR(("copy_to_user() failed in ump_ioctl_switch_hw_usage()\n")); - return -EFAULT; - } -#endif - - return 0; /* success */ -} diff --git a/drivers/gpu/arm/ump/linux/ump_ukk_wrappers.h b/drivers/gpu/arm/ump/linux/ump_ukk_wrappers.h deleted file mode 100644 index ba76e3dbaf9dc5..00000000000000 --- a/drivers/gpu/arm/ump/linux/ump_ukk_wrappers.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2010, 2012-2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** - * @file ump_ukk_wrappers.h - * Defines the wrapper functions which turn Linux IOCTL calls into _ukk_ calls - */ - -#ifndef __UMP_UKK_WRAPPERS_H__ -#define __UMP_UKK_WRAPPERS_H__ - -#include -#include "ump_kernel_common.h" - -#ifdef __cplusplus -extern "C" { -#endif - - - -int ump_get_api_version_wrapper(u32 __user * argument, struct ump_session_data * session_data); -int ump_release_wrapper(u32 __user * argument, struct ump_session_data * session_data); -int ump_size_get_wrapper(u32 __user * argument, struct ump_session_data * session_data); -int ump_msync_wrapper(u32 __user * argument, struct ump_session_data * session_data); -int ump_cache_operations_control_wrapper(u32 __user * argument, struct ump_session_data * session_data); -int ump_switch_hw_usage_wrapper(u32 __user * argument, struct ump_session_data * session_data); -int ump_lock_wrapper(u32 __user * argument, struct ump_session_data * session_data); -int ump_unlock_wrapper(u32 __user * argument, struct ump_session_data * session_data); - - - - -#ifdef __cplusplus -} -#endif - - - -#endif /* __UMP_UKK_WRAPPERS_H__ */ diff --git a/drivers/gpu/arm/umplock/Kbuild b/drivers/gpu/arm/umplock/Kbuild deleted file mode 100644 index 654f1a2e48c4ac..00000000000000 --- a/drivers/gpu/arm/umplock/Kbuild +++ /dev/null @@ -1,13 +0,0 @@ -# -# Copyright (C) 2010-2012 ARM Limited. All rights reserved. -# -# This program is free software and is provided to you under the terms of the GNU General Public License version 2 -# as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. -# -# A copy of the licence is included with the program, and can also be obtained from Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# - -umplock-y = umplock_driver.o - -obj-$(CONFIG_UMPLOCK) := umplock.o diff --git a/drivers/gpu/arm/umplock/Kconfig b/drivers/gpu/arm/umplock/Kconfig deleted file mode 100644 index 2c9b6781839c79..00000000000000 --- a/drivers/gpu/arm/umplock/Kconfig +++ /dev/null @@ -1,5 +0,0 @@ -config UMPLOCK - tristate "UMP locking support" - depends on UMP - help - UMP locking driver support. diff --git a/drivers/gpu/arm/umplock/Makefile b/drivers/gpu/arm/umplock/Makefile deleted file mode 100644 index d1d5c4c7953d41..00000000000000 --- a/drivers/gpu/arm/umplock/Makefile +++ /dev/null @@ -1,69 +0,0 @@ -# -# Copyright (C) 2012 ARM Limited. All rights reserved. -# -# This program is free software and is provided to you under the terms of the GNU General Public License version 2 -# as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. -# -# A copy of the licence is included with the program, and can also be obtained from Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# - -# default to building for the host -ARCH ?= $(shell uname -m) - -# linux build system integration - -ifneq ($(KERNELRELEASE),) -# Inside the kernel build system - -EXTRA_CFLAGS += -I$(KBUILD_EXTMOD) - -SRC = umplock_driver.c - -MODULE:=umplock.ko - -obj-m := $(MODULE:.ko=.o) -$(MODULE:.ko=-y) := $(SRC:.c=.o) - -$(MODULE:.ko=-objs) := $(SRC:.c=.o) - -else -# Outside the kernel build system -# -# - -# Get any user defined KDIR- or maybe even a hardcoded KDIR --include KDIR_CONFIGURATION - -# Define host system directory -KDIR-$(shell uname -m):=/lib/modules/$(shell uname -r)/build - -ifeq ($(ARCH), arm) - # when compiling for ARM we're cross compiling - export CROSS_COMPILE ?= arm-none-linux-gnueabi- - CONFIG ?= arm -else - # Compiling for the host - CONFIG ?= $(shell uname -m) -endif - -# default cpu to select -CPU ?= $(shell uname -m) - -# look up KDIR based om CPU selection -KDIR ?= $(KDIR-$(CPU)) - -ifeq ($(KDIR),) -$(error No KDIR found for platform $(CPU)) -endif - -all: - $(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) - -kernelrelease: - $(MAKE) -C $(KDIR) kernelrelease - -clean: - $(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) clean - -endif diff --git a/drivers/gpu/arm/umplock/umplock_driver.c b/drivers/gpu/arm/umplock/umplock_driver.c deleted file mode 100644 index bd537b3906ad41..00000000000000 --- a/drivers/gpu/arm/umplock/umplock_driver.c +++ /dev/null @@ -1,598 +0,0 @@ -/* - * Copyright (C) 2012-2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include "umplock_ioctl.h" -#include - -#define MAX_ITEMS 1024 -#define MAX_PIDS 128 - -typedef struct lock_cmd_priv { - uint32_t msg[128]; /*ioctl args*/ - u32 pid; /*process id*/ -} _lock_cmd_priv; - -typedef struct lock_ref { - int ref_count; - u32 pid; -} _lock_ref; - -typedef struct umplock_item { - u32 secure_id; - /*u32 references;*/ - _lock_access_usage usage; - _lock_ref references[MAX_PIDS]; - struct semaphore item_lock; -} umplock_item; - -typedef struct umplock_device_private { - struct mutex item_list_lock; - atomic_t sessions; - umplock_item items[MAX_ITEMS]; - u32 pids[MAX_PIDS]; -} umplock_device_private; - -struct umplock_device { - struct cdev cdev; - struct class *umplock_class; -}; - -static char umplock_dev_name[] = "umplock"; - -int umplock_major = 0; -module_param(umplock_major, int, S_IRUGO); /* r--r--r-- */ -MODULE_PARM_DESC(umplock_major, "Device major number"); - -static int umplock_driver_open( struct inode *inode, struct file *filp ); -static int umplock_driver_release( struct inode *inode, struct file *filp ); -static long umplock_driver_ioctl( struct file *f, unsigned int cmd, unsigned long arg ); - -static struct file_operations umplock_fops = { - .owner = THIS_MODULE, - .open = umplock_driver_open, - .release = umplock_driver_release, - .unlocked_ioctl = umplock_driver_ioctl, -}; - -static struct umplock_device umplock_device; -static umplock_device_private device; - -void umplock_init_locklist( void ) -{ - memset(&device.items, 0, sizeof(umplock_item)*MAX_ITEMS); - atomic_set(&device.sessions, 0); -} - -void umplock_deinit_locklist( void ) -{ - memset(&device.items, 0, sizeof(umplock_item)*MAX_ITEMS); -} - -int umplock_device_initialize( void ) -{ - int err; - dev_t dev = 0; - - if ( 0 == umplock_major ) { - err = alloc_chrdev_region(&dev, 0, 1, umplock_dev_name); - umplock_major = MAJOR(dev); - } else { - dev = MKDEV(umplock_major, 0); - err = register_chrdev_region(dev, 1, umplock_dev_name); - } - - if ( 0 == err ) { - memset(&umplock_device, 0, sizeof(umplock_device)); - cdev_init(&umplock_device.cdev, &umplock_fops); - umplock_device.cdev.owner = THIS_MODULE; - umplock_device.cdev.ops = &umplock_fops; - - err = cdev_add(&umplock_device.cdev, dev, 1); - if ( 0 == err ) { - umplock_device.umplock_class = class_create(THIS_MODULE, umplock_dev_name); - if ( IS_ERR(umplock_device.umplock_class ) ) { - err = PTR_ERR(umplock_device.umplock_class); - } else { - struct device *mdev; - mdev = device_create(umplock_device.umplock_class, NULL, dev, NULL, umplock_dev_name); - if ( !IS_ERR(mdev) ) { - return 0; /* all ok */ - } - - err = PTR_ERR(mdev); - class_destroy(umplock_device.umplock_class); - } - cdev_del(&umplock_device.cdev); - } - - unregister_chrdev_region(dev, 1); - } - - return 1; -} - -void umplock_device_terminate(void) -{ - dev_t dev = MKDEV(umplock_major, 0); - - device_destroy(umplock_device.umplock_class, dev); - class_destroy(umplock_device.umplock_class); - - cdev_del(&umplock_device.cdev); - unregister_chrdev_region(dev, 1); -} - -int umplock_constructor(void) -{ - mutex_init(&device.item_list_lock); - if ( !umplock_device_initialize() ) return 1; - umplock_init_locklist(); - - return 0; -} - -void umplock_destructor(void) -{ - umplock_deinit_locklist(); - umplock_device_terminate(); - mutex_destroy(&device.item_list_lock); -} - -int umplock_find_item( u32 secure_id ) -{ - int i; - for ( i=0; imsg; - - i = umplock_find_item(lock_item->secure_id); - - if ( i < 0) - return -1; - - for(j=0; jpid) { - *item_slot = i; - *ref_slot = j; - return 0; - } - } - return -1 ; -} - -static int umplock_find_client_valid(u32 pid) -{ - int i; - - if(pid == 0) - return -1; - - for(i=0; imsg; - - i_index = ref_index = -1; - -#if 0 - if ( lock_item->usage == 1 ) printk( KERN_DEBUG "UMPLOCK: C 0x%x GPU SURFACE\n", lock_item->secure_id ); - else if ( lock_item->usage == 2 ) printk( KERN_DEBUG "UMPLOCK: C 0x%x GPU TEXTURE\n", lock_item->secure_id ); - else printk( KERN_DEBUG "UMPLOCK: C 0x%x CPU\n", lock_item->secure_id ); -#endif - - ret = umplock_find_client_valid( lock_cmd->pid ); - if( ret < 0 ) { - /*lock request from an invalid client pid, do nothing*/ - return 0; - } - - ret = umplock_find_item_by_pid( lock_cmd, &i_index, &ref_index ); - if ( ret >= 0 ) { - if (device.items[i_index].references[ref_index].ref_count == 0) - device.items[i_index].references[ref_index].ref_count = 1; - } else if ( (i_index = umplock_find_item( lock_item->secure_id)) >= 0 ) { - for ( ref_index = 0; ref_index < MAX_PIDS; ref_index++) { - if (device.items[i_index].references[ref_index].pid == 0) break; - } - if ( ref_index < MAX_PIDS ) { - device.items[i_index].references[ref_index].pid = lock_cmd->pid; - device.items[i_index].references[ref_index].ref_count = 1; - } else { - printk( KERN_ERR "UMPLOCK: whoops, item ran out of available reference slot\n" ); - } - } else { - i_index = umplock_find_slot(); - - if ( i_index >= 0 ) { - device.items[i_index].secure_id = lock_item->secure_id; - device.items[i_index].usage = lock_item->usage; - device.items[i_index].references[0].pid = lock_cmd->pid; - device.items[i_index].references[0].ref_count = 1; - sema_init(&device.items[i_index].item_lock, 1); - } else { - printk( KERN_ERR "UMPLOCK: whoops, ran out of available slots\n" ); - } - } - - return 0; -} -/** IOCTLs **/ - -static int do_umplock_create(_lock_cmd_priv *lock_cmd) -{ - int ret = 0; - mutex_lock(&device.item_list_lock); - ret = do_umplock_create_locked(lock_cmd); - mutex_unlock(&device.item_list_lock); - return ret; -} - -static int do_umplock_process( _lock_cmd_priv *lock_cmd ) -{ - int ret, i_index, ref_index, ref_count; - _lock_item_s *lock_item = (_lock_item_s *)&lock_cmd->msg; - - mutex_lock(&device.item_list_lock); - - do_umplock_create_locked(lock_cmd); - - ret = umplock_find_client_valid( lock_cmd->pid ); - if( ret < 0 ) { - /*lock request from an invalid client pid, do nothing*/ - mutex_unlock(&device.item_list_lock); - return 0; - } - - ret = umplock_find_item_by_pid( lock_cmd, &i_index, &ref_index ); - ref_count = device.items[i_index].references[ref_index].ref_count; - if ( ret >= 0 ) { - if (ref_count == 1) { - /*add ref before down to wait for the umplock*/ - device.items[i_index].references[ref_index].ref_count++; - mutex_unlock(&device.item_list_lock); - if ( down_interruptible(&device.items[i_index].item_lock) ) { - /*wait up without hold the umplock. restore previous state and return*/ - mutex_lock(&device.item_list_lock); - device.items[i_index].references[ref_index].ref_count--; - mutex_unlock(&device.item_list_lock); - return -ERESTARTSYS; - } - mutex_lock(&device.item_list_lock); - } else { - /*already got the umplock, add ref*/ - device.items[i_index].references[ref_index].ref_count++; - } -#if 0 - if ( lock_item->usage == 1 ) printk( KERN_DEBUG "UMPLOCK: P 0x%x GPU SURFACE\n", lock_item->secure_id ); - else if ( lock_item->usage == 2 ) printk( KERN_DEBUG "UMPLOCK: P 0x%x GPU TEXTURE\n", lock_item->secure_id ); - else printk( KERN_DEBUG "UMPLOCK: P 0x%x CPU\n", lock_item->secure_id ); -#endif - } else { - /*fail to find a item*/ - printk(KERN_ERR "UMPLOCK: IOCTL_UMPLOCK_PROCESS called with invalid parameter\n"); - mutex_unlock(&device.item_list_lock); - return -EINVAL; - } - mutex_unlock(&device.item_list_lock); - return 0; -} - -static int do_umplock_release( _lock_cmd_priv *lock_cmd ) -{ - int i_index,ref_index, ref_count; - int ret; - _lock_item_s *lock_item = (_lock_item_s *)&lock_cmd->msg; - - mutex_lock(&device.item_list_lock); - ret = umplock_find_client_valid( lock_cmd->pid ); - if( ret < 0 ) { - /*lock request from an invalid client pid, do nothing*/ - mutex_unlock(&device.item_list_lock); - return 0; - } - - i_index = ref_index = -1; - - ret = umplock_find_item_by_pid( lock_cmd, &i_index, &ref_index ); - - if ( ret >= 0 ) { - device.items[i_index].references[ref_index].ref_count--; - ref_count = device.items[i_index].references[ref_index].ref_count; - -#if 0 - if ( lock_item->usage == 1 ) printk( KERN_DEBUG "UMPLOCK: R 0x%x GPU SURFACE\n", lock_item->secure_id ); - else if ( lock_item->usage == 2 ) printk( KERN_DEBUG "UMPLOCK: R 0x%x GPU TEXTURE\n", lock_item->secure_id ); - else printk( KERN_DEBUG "UMPLOCK: R 0x%x CPU\n", lock_item->secure_id ); -#endif - /*reached the last reference to the umplock*/ - if ( ref_count == 1 ) { - /*release the umplock*/ - up( &device.items[i_index].item_lock ); - - device.items[i_index].references[ref_index].ref_count = 0; - device.items[i_index].references[ref_index].pid = 0; - } - } else { - /*fail to find item*/ - printk(KERN_ERR "UMPLOCK: IOCTL_UMPLOCK_RELEASE called with invalid parameter\n"); - mutex_unlock(&device.item_list_lock); - return -EINVAL; - } - mutex_unlock(&device.item_list_lock); - return 0; -} - -static int do_umplock_zap( void ) -{ - int i; - - printk( KERN_DEBUG "UMPLOCK: ZAP ALL ENTRIES!\n" ); - - mutex_lock(&device.item_list_lock); - - for ( i=0; isecure_id=%d\t reference[%d].ref_count=%d.pid=%d\n", - i, - device.items[i].secure_id, - j, - device.items[i].references[j].ref_count, - device.items[i].references[j].pid); - } - } - } - mutex_unlock(&device.item_list_lock); - - return 0; -} - -int do_umplock_client_add (_lock_cmd_priv *lock_cmd ) -{ - int i; - mutex_lock(&device.item_list_lock); - for ( i= 0; ipid) { - return 0; - } - } - for ( i=0; ipid; - break; - } - } - mutex_unlock(&device.item_list_lock); - if( i==MAX_PIDS) { - printk(KERN_ERR "Oops, Run out of cient slots\n "); - } - return 0; -} - -int do_umplock_client_delete (_lock_cmd_priv *lock_cmd ) -{ - int p_index=-1, i_index=-1,ref_index=-1; - int ret; - _lock_item_s *lock_item; - lock_item = (_lock_item_s *)&lock_cmd->msg; - - mutex_lock(&device.item_list_lock); - p_index = umplock_find_client_valid( lock_cmd->pid ); - /*lock item pid is not valid.*/ - if ( p_index<0 ) { - mutex_unlock(&device.item_list_lock); - return 0; - } - - /*walk through umplock item list and release reference attached to this client*/ - for(i_index = 0; i_index< MAX_ITEMS; i_index++ ) { - lock_item->secure_id = device.items[i_index].secure_id; - /*find the item index and reference slot for the lock_item*/ - ret = umplock_find_item_by_pid(lock_cmd, &i_index, &ref_index); - - if(ret < 0) { - /*client has no reference on this umplock item, skip*/ - continue; - } - while(device.items[i_index].references[ref_index].ref_count) { - /*release references on this client*/ - mutex_unlock(&device.item_list_lock); - do_umplock_release(lock_cmd); - mutex_lock(&device.item_list_lock); - } - } - - /*remove the pid from umplock valid pid list*/ - device.pids[p_index] = 0; - mutex_unlock(&device.item_list_lock); - - return 0; -} - -static long umplock_driver_ioctl( struct file *f, unsigned int cmd, unsigned long arg ) -{ - int ret; - uint32_t size = _IOC_SIZE(cmd); - _lock_cmd_priv lock_cmd ; - - if (_IOC_TYPE(cmd) != LOCK_IOCTL_GROUP ) { - return -ENOTTY; - } - - if (_IOC_NR(cmd) >= LOCK_IOCTL_MAX_CMDS ) { - return -ENOTTY; - } - - switch ( cmd ) { - case LOCK_IOCTL_CREATE: - if (size != sizeof(_lock_item_s)) { - return -ENOTTY; - } - - if (copy_from_user(&lock_cmd.msg, (void __user *)arg, size)) { - return -EFAULT; - } - lock_cmd.pid = (u32)current->tgid; - ret = do_umplock_create(&lock_cmd); - if (ret) { - return ret; - } - return 0; - - case LOCK_IOCTL_PROCESS: - if (size != sizeof(_lock_item_s)) { - return -ENOTTY; - } - - if (copy_from_user(&lock_cmd.msg, (void __user *)arg, size)) { - return -EFAULT; - } - lock_cmd.pid = (u32)current->tgid; - return do_umplock_process(&lock_cmd); - - case LOCK_IOCTL_RELEASE: - if (size != sizeof(_lock_item_s)) { - return -ENOTTY; - } - - if (copy_from_user(&lock_cmd.msg, (void __user *)arg, size)) { - return -EFAULT; - } - lock_cmd.pid = (u32)current->tgid; - ret = do_umplock_release( &lock_cmd ); - if (ret) { - return ret; - } - return 0; - - case LOCK_IOCTL_ZAP: - do_umplock_zap(); - return 0; - - case LOCK_IOCTL_DUMP: - do_umplock_dump(); - return 0; - } - - return -ENOIOCTLCMD; -} - -static int umplock_driver_open( struct inode *inode, struct file *filp ) -{ - _lock_cmd_priv lock_cmd; - - atomic_inc(&device.sessions); - printk( KERN_DEBUG "UMPLOCK: OPEN SESSION (%i references)\n", atomic_read(&device.sessions) ); - - lock_cmd.pid = (u32)current->tgid; - do_umplock_client_add(&lock_cmd); - - return 0; -} - -static int umplock_driver_release( struct inode *inode, struct file *filp ) -{ - _lock_cmd_priv lock_cmd; - - lock_cmd.pid = (u32)current->tgid; - do_umplock_client_delete(&lock_cmd); - - atomic_dec(&device.sessions); - printk( KERN_DEBUG "UMPLOCK: CLOSE SESSION (%i references)\n", atomic_read(&device.sessions) ); - if ( atomic_read(&device.sessions) == 0 ) { - do_umplock_zap(); - } - - return 0; -} - -static int __init umplock_initialize_module( void ) -{ - printk( KERN_DEBUG "Inserting UMP lock device driver. Compiled: %s, time: %s\n", __DATE__, __TIME__ ); - - if ( !umplock_constructor() ) { - printk( KERN_ERR "UMP lock device driver init failed\n"); - return -ENOTTY; - } - - printk( KERN_DEBUG "UMP lock device driver loaded\n" ); - - return 0; -} - -static void __exit umplock_cleanup_module( void ) -{ - printk( KERN_DEBUG "unloading UMP lock module\n" ); - umplock_destructor(); - printk( KERN_DEBUG "UMP lock module unloaded\n" ); -} - -module_init(umplock_initialize_module); -module_exit(umplock_cleanup_module); - - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("ARM Ltd."); -MODULE_DESCRIPTION("ARM UMP locker"); diff --git a/drivers/gpu/arm/umplock/umplock_ioctl.h b/drivers/gpu/arm/umplock/umplock_ioctl.h deleted file mode 100644 index 88b0a46fff4f16..00000000000000 --- a/drivers/gpu/arm/umplock/umplock_ioctl.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2012-2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef __UMPLOCK_IOCTL_H__ -#define __UMPLOCK_IOCTL_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -#ifndef __user -#define __user -#endif - - -/** - * @file umplock_ioctl.h - * This file describes the interface needed to use the Linux device driver. - * The interface is used by the userpace Mali DDK. - */ - -typedef enum { - _LOCK_ACCESS_RENDERABLE = 1, - _LOCK_ACCESS_TEXTURE, - _LOCK_ACCESS_CPU_WRITE, - _LOCK_ACCESS_CPU_READ, -} _lock_access_usage; - -typedef struct _lock_item_s { - unsigned int secure_id; - _lock_access_usage usage; -} _lock_item_s; - - -#define LOCK_IOCTL_GROUP 0x91 - -#define _LOCK_IOCTL_CREATE_CMD 0 /* create kernel lock item */ -#define _LOCK_IOCTL_PROCESS_CMD 1 /* process kernel lock item */ -#define _LOCK_IOCTL_RELEASE_CMD 2 /* release kernel lock item */ -#define _LOCK_IOCTL_ZAP_CMD 3 /* clean up all kernel lock items */ -#define _LOCK_IOCTL_DUMP_CMD 4 /* dump all the items */ - -#define LOCK_IOCTL_MAX_CMDS 5 - -#define LOCK_IOCTL_CREATE _IOW( LOCK_IOCTL_GROUP, _LOCK_IOCTL_CREATE_CMD, _lock_item_s ) -#define LOCK_IOCTL_PROCESS _IOW( LOCK_IOCTL_GROUP, _LOCK_IOCTL_PROCESS_CMD, _lock_item_s ) -#define LOCK_IOCTL_RELEASE _IOW( LOCK_IOCTL_GROUP, _LOCK_IOCTL_RELEASE_CMD, _lock_item_s ) -#define LOCK_IOCTL_ZAP _IO ( LOCK_IOCTL_GROUP, _LOCK_IOCTL_ZAP_CMD ) -#define LOCK_IOCTL_DUMP _IO ( LOCK_IOCTL_GROUP, _LOCK_IOCTL_DUMP_CMD ) - -#ifdef __cplusplus -} -#endif - -#endif /* __UMPLOCK_IOCTL_H__ */ - diff --git a/include/ump/ump_kernel_interface.h b/include/ump/ump_kernel_interface.h deleted file mode 100644 index 99cc697334b0ed..00000000000000 --- a/include/ump/ump_kernel_interface.h +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright (C) 2010, 2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** - * @file ump_kernel_interface.h - * - * This file contains the kernel space part of the UMP API. - */ - -#ifndef __UMP_KERNEL_INTERFACE_H__ -#define __UMP_KERNEL_INTERFACE_H__ - - -/** @defgroup ump_kernel_space_api UMP Kernel Space API - * @{ */ - - -#include "ump_kernel_platform.h" - - -#ifdef __cplusplus -extern "C" { -#endif - - -/** - * External representation of a UMP handle in kernel space. - */ -typedef void *ump_dd_handle; - -/** - * Typedef for a secure ID, a system wide identificator for UMP memory buffers. - */ -typedef unsigned int ump_secure_id; - - -/** - * Value to indicate an invalid UMP memory handle. - */ -#define UMP_DD_HANDLE_INVALID ((ump_dd_handle)0) - - -/** - * Value to indicate an invalid secure Id. - */ -#define UMP_INVALID_SECURE_ID ((ump_secure_id)-1) - - -/** - * UMP error codes for kernel space. - */ -typedef enum -{ - UMP_DD_SUCCESS, /**< indicates success */ - UMP_DD_INVALID, /**< indicates failure */ -} ump_dd_status_code; - - -/** - * Struct used to describe a physical block used by UMP memory - */ -typedef struct ump_dd_physical_block -{ - unsigned long addr; /**< The physical address of the block */ - unsigned long size; /**< The length of the block, typically page aligned */ -} ump_dd_physical_block; - - -/** - * Retrieves the secure ID for the specified UMP memory. - * - * This identificator is unique across the entire system, and uniquely identifies - * the specified UMP memory. This identificator can later be used through the - * @ref ump_dd_handle_create_from_secure_id "ump_dd_handle_create_from_secure_id" or - * @ref ump_handle_create_from_secure_id "ump_handle_create_from_secure_id" - * functions in order to access this UMP memory, for instance from another process. - * - * @note There is a user space equivalent function called @ref ump_secure_id_get "ump_secure_id_get" - * - * @see ump_dd_handle_create_from_secure_id - * @see ump_handle_create_from_secure_id - * @see ump_secure_id_get - * - * @param mem Handle to UMP memory. - * - * @return Returns the secure ID for the specified UMP memory. - */ -UMP_KERNEL_API_EXPORT ump_secure_id ump_dd_secure_id_get(ump_dd_handle mem); - - -/** - * Retrieves a handle to allocated UMP memory. - * - * The usage of UMP memory is reference counted, so this will increment the reference - * count by one for the specified UMP memory. - * Use @ref ump_dd_reference_release "ump_dd_reference_release" when there is no longer any - * use for the retrieved handle. - * - * @note There is a user space equivalent function called @ref ump_handle_create_from_secure_id "ump_handle_create_from_secure_id" - * - * @see ump_dd_reference_release - * @see ump_handle_create_from_secure_id - * - * @param secure_id The secure ID of the UMP memory to open, that can be retrieved using the @ref ump_secure_id_get "ump_secure_id_get " function. - * - * @return UMP_INVALID_MEMORY_HANDLE indicates failure, otherwise a valid handle is returned. - */ -UMP_KERNEL_API_EXPORT ump_dd_handle ump_dd_handle_create_from_secure_id(ump_secure_id secure_id); - - -/** - * Retrieves the number of physical blocks used by the specified UMP memory. - * - * This function retrieves the number of @ref ump_dd_physical_block "ump_dd_physical_block" structs needed - * to describe the physical memory layout of the given UMP memory. This can later be used when calling - * the functions @ref ump_dd_phys_blocks_get "ump_dd_phys_blocks_get" and - * @ref ump_dd_phys_block_get "ump_dd_phys_block_get". - * - * @see ump_dd_phys_blocks_get - * @see ump_dd_phys_block_get - * - * @param mem Handle to UMP memory. - * - * @return The number of ump_dd_physical_block structs required to describe the physical memory layout of the specified UMP memory. - */ -UMP_KERNEL_API_EXPORT unsigned long ump_dd_phys_block_count_get(ump_dd_handle mem); - - -/** - * Retrieves all physical memory block information for specified UMP memory. - * - * This function can be used by other device drivers in order to create MMU tables. - * - * @note This function will fail if the num_blocks parameter is either to large or to small. - * - * @see ump_dd_phys_block_get - * - * @param mem Handle to UMP memory. - * @param blocks An array of @ref ump_dd_physical_block "ump_dd_physical_block" structs that will receive the physical description. - * @param num_blocks The number of blocks to return in the blocks array. Use the function - * @ref ump_dd_phys_block_count_get "ump_dd_phys_block_count_get" first to determine the number of blocks required. - * - * @return UMP_DD_SUCCESS indicates success, UMP_DD_INVALID indicates failure. - */ -UMP_KERNEL_API_EXPORT ump_dd_status_code ump_dd_phys_blocks_get(ump_dd_handle mem, ump_dd_physical_block *blocks, unsigned long num_blocks); - - -/** - * Retrieves the physical memory block information for specified block for the specified UMP memory. - * - * This function can be used by other device drivers in order to create MMU tables. - * - * @note This function will return UMP_DD_INVALID if the specified index is out of range. - * - * @see ump_dd_phys_blocks_get - * - * @param mem Handle to UMP memory. - * @param index Which physical info block to retrieve. - * @param block Pointer to a @ref ump_dd_physical_block "ump_dd_physical_block" struct which will receive the requested information. - * - * @return UMP_DD_SUCCESS indicates success, UMP_DD_INVALID indicates failure. - */ -UMP_KERNEL_API_EXPORT ump_dd_status_code ump_dd_phys_block_get(ump_dd_handle mem, unsigned long index, ump_dd_physical_block *block); - - -/** - * Retrieves the actual size of the specified UMP memory. - * - * The size is reported in bytes, and is typically page aligned. - * - * @note There is a user space equivalent function called @ref ump_size_get "ump_size_get" - * - * @see ump_size_get - * - * @param mem Handle to UMP memory. - * - * @return Returns the allocated size of the specified UMP memory, in bytes. - */ -UMP_KERNEL_API_EXPORT unsigned long ump_dd_size_get(ump_dd_handle mem); - - -/** - * Adds an extra reference to the specified UMP memory. - * - * This function adds an extra reference to the specified UMP memory. This function should - * be used every time a UMP memory handle is duplicated, that is, assigned to another ump_dd_handle - * variable. The function @ref ump_dd_reference_release "ump_dd_reference_release" must then be used - * to release each copy of the UMP memory handle. - * - * @note You are not required to call @ref ump_dd_reference_add "ump_dd_reference_add" - * for UMP handles returned from - * @ref ump_dd_handle_create_from_secure_id "ump_dd_handle_create_from_secure_id", - * because these handles are already reference counted by this function. - * - * @note There is a user space equivalent function called @ref ump_reference_add "ump_reference_add" - * - * @see ump_reference_add - * - * @param mem Handle to UMP memory. - */ -UMP_KERNEL_API_EXPORT void ump_dd_reference_add(ump_dd_handle mem); - - -/** - * Releases a reference from the specified UMP memory. - * - * This function should be called once for every reference to the UMP memory handle. - * When the last reference is released, all resources associated with this UMP memory - * handle are freed. - * - * @note There is a user space equivalent function called @ref ump_reference_release "ump_reference_release" - * - * @see ump_reference_release - * - * @param mem Handle to UMP memory. - */ -UMP_KERNEL_API_EXPORT void ump_dd_reference_release(ump_dd_handle mem); - - -#ifdef __cplusplus -} -#endif - - -/** @} */ /* end group ump_kernel_space_api */ - - -#endif /* __UMP_KERNEL_INTERFACE_H__ */ diff --git a/include/ump/ump_kernel_interface_ref_drv.h b/include/ump/ump_kernel_interface_ref_drv.h deleted file mode 100644 index 8a65302c3e1fa0..00000000000000 --- a/include/ump/ump_kernel_interface_ref_drv.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2010, 2013 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** - * @file ump_kernel_interface.h - */ - -#ifndef __UMP_KERNEL_INTERFACE_REF_DRV_H__ -#define __UMP_KERNEL_INTERFACE_REF_DRV_H__ - -#include "ump_kernel_interface.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** Turn specified physical memory into UMP memory. */ -UMP_KERNEL_API_EXPORT ump_dd_handle ump_dd_handle_create_from_phys_blocks(ump_dd_physical_block *blocks, unsigned long num_blocks); - -#ifdef __cplusplus -} -#endif - -#endif /* __UMP_KERNEL_INTERFACE_REF_DRV_H__ */ diff --git a/include/ump/ump_kernel_platform.h b/include/ump/ump_kernel_platform.h deleted file mode 100644 index 06a54c33ec147d..00000000000000 --- a/include/ump/ump_kernel_platform.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2010 ARM Limited. All rights reserved. - * - * This program is free software and is provided to you under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. - * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** - * @file ump_kernel_platform.h - * - * This file should define UMP_KERNEL_API_EXPORT, - * which dictates how the UMP kernel API should be exported/imported. - * Modify this file, if needed, to match your platform setup. - */ - -#ifndef __UMP_KERNEL_PLATFORM_H__ -#define __UMP_KERNEL_PLATFORM_H__ - -/** @addtogroup ump_kernel_space_api - * @{ */ - -/** - * A define which controls how UMP kernel space API functions are imported and exported. - * This define should be set by the implementor of the UMP API. - */ - -#define UMP_KERNEL_API_EXPORT - -/** @} */ /* end group ump_kernel_space_api */ - - -#endif /* __UMP_KERNEL_PLATFORM_H__ */ From e3280826bbb87a7719ef457238a9c09d38741131 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Thu, 14 Aug 2014 17:48:04 +0100 Subject: [PATCH 752/788] clk: samsung: exynos4: add Exynos4412 VPLL rates These are common VESA/CEA pixel clock frequencies. --- drivers/clk/samsung/clk-exynos4.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index 864b5e21ac3a7b..a03135f0f5b550 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -1331,8 +1331,19 @@ static struct samsung_pll_rate_table exynos4x12_vpll_rates[] __initdata = { PLL_36XX_RATE(350000000, 175, 3, 2, 0), PLL_36XX_RATE(266000000, 133, 3, 2, 0), PLL_36XX_RATE(160000000, 160, 3, 3, 0), + PLL_36XX_RATE(148500000, 74, 3, 2, 16384), + PLL_36XX_RATE(135000000, 68, 3, 2, 32768), + PLL_36XX_RATE(108000000, 54, 3, 2, 0), PLL_36XX_RATE(106031250, 53, 3, 2, 1024), + PLL_36XX_RATE( 85500000, 43, 3, 2, 49152), + PLL_36XX_RATE( 78750000, 39, 3, 2, 24576), + PLL_36XX_RATE( 74250000, 37, 3, 2, 8192), + PLL_36XX_RATE( 65000000, 33, 3, 2, 32768), PLL_36XX_RATE( 53015625, 53, 3, 3, 1024), + PLL_36XX_RATE( 49500000, 50, 3, 3, 32768), + PLL_36XX_RATE( 40000000, 40, 3, 3, 0), + PLL_36XX_RATE( 31500000, 32, 3, 3, 32768), + PLL_36XX_RATE( 25175000, 25, 3, 3, 11469), { /* sentinel */ } }; From d5b911f2484e612231aebb4be5471d78b7e662dd Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Tue, 9 Sep 2014 14:50:27 -0600 Subject: [PATCH 753/788] clk: samsung: exynos4: add extra VPLL frequencies Add high frequencies to test against the Hitachi VGA monitor at the ODM, to try to get a feel for the maximum pixel clock of the FIMD. 1600x1200 @ 75Hz : 202.5MHz 1600x1200 @ 85Hz : 229.5MHz 1600x1200 @ 90Hz : 243.0MHz --- drivers/clk/samsung/clk-exynos4.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index a03135f0f5b550..3428b8f0c02d94 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -1330,6 +1330,9 @@ static struct samsung_pll_rate_table exynos4x12_vpll_rates[] __initdata = { PLL_36XX_RATE(440000000, 110, 3, 1, 0), PLL_36XX_RATE(350000000, 175, 3, 2, 0), PLL_36XX_RATE(266000000, 133, 3, 2, 0), + PLL_36XX_RATE(243000000, 122, 3, 2, 32768), + PLL_36XX_RATE(229500000, 115, 3, 2, 49152), + PLL_36XX_RATE(202500000, 101, 3, 2, 16384), PLL_36XX_RATE(160000000, 160, 3, 3, 0), PLL_36XX_RATE(148500000, 74, 3, 2, 16384), PLL_36XX_RATE(135000000, 68, 3, 2, 32768), From 0a2ceef4b554ff6d596940cf85ba0b7cd2b48b0d Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Thu, 14 Aug 2014 11:10:54 +0100 Subject: [PATCH 754/788] mali: prevent build with 64-bit DMA addresses Mali uses pointers to store DMA addresses (mali_io_address), which are naturally 32-bit on this platform. So when the kernel is built with 64-bit dma_addr_t, Mali fails in a cryptic way. mali_allocate_empty_page() calls mali_mmu_get_table_page(), which leads to mali_mem_os_get_table_page(), where we pass a 32-bit mali_io_address as the phys (dma_addr_t) parameter to dma_alloc_writecombine. Shortly after, when the DMA layer comes to write a 64-bit value into this variable, it ends up spilling over the allocated 32-bits and causing stack corruption. In this case, the stack corruption hits mali_mmu_initialize(), causing it to lose the address where it was going to store the result from mali_empty_page_directory_phys(). It tries to write it into address 0, and the resultant boot-time crash looks like: Unable to handle kernel NULL pointer dereference at virtual address 00000000 PC is at mali_mmu_initialize+0x24/0x118 LR is at mali_allocate_empty_page+0x114/0x128 pc : [] lr : [] psr: 20000113 sp : c7079dd8 ip : 00000000 fp : c0953330 r10: c7491200 r9 : 00000000 r8 : c7228210 r7 : c0a1079c r6 : 00000000 r5 : c0aa5e04 r4 : 00000000 r3 : 00000a6b r2 : c9843000 r1 : 60000193 r0 : 49843000 Flags: nzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel Control: 10c5387d Table: 4000404a DAC: 00000015 [] (mali_mmu_initialize) from [] (mali_initialize_subsystems+0x2fc/0x810) [] (mali_initialize_subsystems) from [] (mali_probe+0x84/0x708) [] (mali_probe) from [] (platform_drv_probe+0x2c/0x5c) Add a compile check here to avoid wasting time on this again... --- drivers/gpu/arm/mali/common/mali_osk_types.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/arm/mali/common/mali_osk_types.h b/drivers/gpu/arm/mali/common/mali_osk_types.h index 6d64cd91fce473..ae74a431156e57 100644 --- a/drivers/gpu/arm/mali/common/mali_osk_types.h +++ b/drivers/gpu/arm/mali/common/mali_osk_types.h @@ -291,6 +291,9 @@ typedef struct _mali_osk_mutex_rw_s _mali_osk_mutex_rw_t; * _mali_osk_mem_iowrite32() functions. */ typedef struct _mali_io_address * mali_io_address; +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT +#error Mali is incompatible with 64-bit dma_addr_t +#endif /** @defgroup _MALI_OSK_CPU_PAGE CPU Physical page size macros. * From 84e022cb3b9808c2a136fb4f0d7ad7cbcc981fca Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Wed, 9 Jul 2014 10:06:06 +0100 Subject: [PATCH 755/788] mali: implement initialization from device tree Based on work by Alban Browaeys . --- .../gpu/arm/mali/common/mali_kernel_core.c | 127 +++++++-------- drivers/gpu/arm/mali/common/mali_osk_mali.h | 4 + .../arm/mali/include/linux/mali/mali_utgard.h | 40 +++++ .../gpu/arm/mali/linux/mali_kernel_linux.c | 147 ++++++++++++++++++ .../gpu/arm/mali/linux/mali_kernel_linux.h | 1 + drivers/gpu/arm/mali/linux/mali_osk_mali.c | 33 +++- 6 files changed, 277 insertions(+), 75 deletions(-) diff --git a/drivers/gpu/arm/mali/common/mali_kernel_core.c b/drivers/gpu/arm/mali/common/mali_kernel_core.c index e0994246399e5b..fee93550919d1d 100644 --- a/drivers/gpu/arm/mali/common/mali_kernel_core.c +++ b/drivers/gpu/arm/mali/common/mali_kernel_core.c @@ -117,20 +117,10 @@ static _mali_osk_errcode_t mali_parse_product_info(void) * Look at the version register for the first PP core in order to determine the GPU HW revision. */ - u32 first_pp_offset; _mali_osk_resource_t first_pp_resource; - /* Find out where the first PP core is located */ - if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x8000, NULL)) { - /* Mali-300/400/450 */ - first_pp_offset = 0x8000; - } else { - /* Mali-200 */ - first_pp_offset = 0x0000; - } - - /* Find the first PP core resource (again) */ - if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + first_pp_offset, &first_pp_resource)) { + /* Find the first PP core resource */ + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_0, &first_pp_resource)) { /* Create a dummy PP object for this core so that we can read the version register */ struct mali_group *group = mali_group_create(NULL, NULL, NULL); if (NULL != group) { @@ -186,40 +176,29 @@ static void mali_resource_count(u32 *pp_count, u32 *l2_count) *pp_count = 0; *l2_count = 0; - if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x08000, NULL)) { + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_0, NULL)) ++(*pp_count); - } - if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x0A000, NULL)) { + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_1, NULL)) ++(*pp_count); - } - if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x0C000, NULL)) { + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_2, NULL)) ++(*pp_count); - } - if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x0E000, NULL)) { + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_3, NULL)) ++(*pp_count); - } - if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x28000, NULL)) { + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_4, NULL)) ++(*pp_count); - } - if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x2A000, NULL)) { + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_5, NULL)) ++(*pp_count); - } - if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x2C000, NULL)) { + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_6, NULL)) ++(*pp_count); - } - if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x2E000, NULL)) { + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_7, NULL)) ++(*pp_count); - } - if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x1000, NULL)) { + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_L2_GP, NULL)) ++(*l2_count); - } - if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x10000, NULL)) { + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_L2_PP_GRP0, NULL)) ++(*l2_count); - } - if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x11000, NULL)) { + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_L2_PP_GRP1, NULL)) ++(*l2_count); - } } static void mali_delete_groups(void) @@ -273,7 +252,7 @@ static _mali_osk_errcode_t mali_parse_config_l2_cache(void) if (mali_is_mali400()) { _mali_osk_resource_t l2_resource; - if (_MALI_OSK_ERR_OK != _mali_osk_resource_find(global_gpu_base_address + 0x1000, &l2_resource)) { + if (_MALI_OSK_ERR_OK != _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_L2, &l2_resource)) { MALI_DEBUG_PRINT(3, ("Did not find required Mali L2 cache in config file\n")); return _MALI_OSK_ERR_FAULT; } @@ -295,7 +274,7 @@ static _mali_osk_errcode_t mali_parse_config_l2_cache(void) _mali_osk_resource_t l2_pp_grp1_resource; /* Make cluster for GP's L2 */ - if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x10000, &l2_gp_resource)) { + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_L2_GP, &l2_gp_resource)) { MALI_DEBUG_PRINT(3, ("Creating Mali-450 L2 cache core for GP\n")); l2_cache = mali_create_l2_cache_core(&l2_gp_resource); if (NULL == l2_cache) { @@ -308,7 +287,7 @@ static _mali_osk_errcode_t mali_parse_config_l2_cache(void) } /* Find corresponding l2 domain */ - if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x1000, &l2_pp_grp0_resource)) { + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_L2_PP_GRP0, &l2_pp_grp0_resource)) { MALI_DEBUG_PRINT(3, ("Creating Mali-450 L2 cache core for PP group 0\n")); l2_cache = mali_create_l2_cache_core(&l2_pp_grp0_resource); if (NULL == l2_cache) { @@ -321,7 +300,7 @@ static _mali_osk_errcode_t mali_parse_config_l2_cache(void) } /* Second PP core group is optional, don't fail if we don't find it */ - if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x11000, &l2_pp_grp1_resource)) { + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_L2_PP_GRP1, &l2_pp_grp1_resource)) { MALI_DEBUG_PRINT(3, ("Creating Mali-450 L2 cache core for PP group 1\n")); l2_cache = mali_create_l2_cache_core(&l2_pp_grp1_resource); if (NULL == l2_cache) { @@ -496,31 +475,31 @@ static _mali_osk_errcode_t mali_parse_config_groups(void) cluster_id_pp_grp1 = 2; } - resource_gp_found = _mali_osk_resource_find(global_gpu_base_address + 0x00000, &resource_gp); - resource_gp_mmu_found = _mali_osk_resource_find(global_gpu_base_address + 0x03000, &resource_gp_mmu); - resource_pp_found[0] = _mali_osk_resource_find(global_gpu_base_address + 0x08000, &(resource_pp[0])); - resource_pp_found[1] = _mali_osk_resource_find(global_gpu_base_address + 0x0A000, &(resource_pp[1])); - resource_pp_found[2] = _mali_osk_resource_find(global_gpu_base_address + 0x0C000, &(resource_pp[2])); - resource_pp_found[3] = _mali_osk_resource_find(global_gpu_base_address + 0x0E000, &(resource_pp[3])); - resource_pp_found[4] = _mali_osk_resource_find(global_gpu_base_address + 0x28000, &(resource_pp[4])); - resource_pp_found[5] = _mali_osk_resource_find(global_gpu_base_address + 0x2A000, &(resource_pp[5])); - resource_pp_found[6] = _mali_osk_resource_find(global_gpu_base_address + 0x2C000, &(resource_pp[6])); - resource_pp_found[7] = _mali_osk_resource_find(global_gpu_base_address + 0x2E000, &(resource_pp[7])); - resource_pp_mmu_found[0] = _mali_osk_resource_find(global_gpu_base_address + 0x04000, &(resource_pp_mmu[0])); - resource_pp_mmu_found[1] = _mali_osk_resource_find(global_gpu_base_address + 0x05000, &(resource_pp_mmu[1])); - resource_pp_mmu_found[2] = _mali_osk_resource_find(global_gpu_base_address + 0x06000, &(resource_pp_mmu[2])); - resource_pp_mmu_found[3] = _mali_osk_resource_find(global_gpu_base_address + 0x07000, &(resource_pp_mmu[3])); - resource_pp_mmu_found[4] = _mali_osk_resource_find(global_gpu_base_address + 0x1C000, &(resource_pp_mmu[4])); - resource_pp_mmu_found[5] = _mali_osk_resource_find(global_gpu_base_address + 0x1D000, &(resource_pp_mmu[5])); - resource_pp_mmu_found[6] = _mali_osk_resource_find(global_gpu_base_address + 0x1E000, &(resource_pp_mmu[6])); - resource_pp_mmu_found[7] = _mali_osk_resource_find(global_gpu_base_address + 0x1F000, &(resource_pp_mmu[7])); + resource_gp_found = _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_GP, &resource_gp); + resource_gp_mmu_found = _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_GP_MMU, &resource_gp_mmu); + resource_pp_found[0] = _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_0, &(resource_pp[0])); + resource_pp_found[1] = _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_1, &(resource_pp[1])); + resource_pp_found[2] = _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_2, &(resource_pp[2])); + resource_pp_found[3] = _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_3, &(resource_pp[3])); + resource_pp_found[4] = _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_4, &(resource_pp[4])); + resource_pp_found[5] = _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_5, &(resource_pp[5])); + resource_pp_found[6] = _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_6, &(resource_pp[6])); + resource_pp_found[7] = _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_7, &(resource_pp[7])); + resource_pp_mmu_found[0] = _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_MMU_0, &(resource_pp_mmu[0])); + resource_pp_mmu_found[1] = _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_MMU_1, &(resource_pp_mmu[1])); + resource_pp_mmu_found[2] = _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_MMU_2, &(resource_pp_mmu[2])); + resource_pp_mmu_found[3] = _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_MMU_3, &(resource_pp_mmu[3])); + resource_pp_mmu_found[4] = _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_MMU_4, &(resource_pp_mmu[4])); + resource_pp_mmu_found[5] = _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_MMU_5, &(resource_pp_mmu[5])); + resource_pp_mmu_found[6] = _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_MMU_6, &(resource_pp_mmu[6])); + resource_pp_mmu_found[7] = _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_MMU_7, &(resource_pp_mmu[7])); if (mali_is_mali450()) { - resource_bcast_found = _mali_osk_resource_find(global_gpu_base_address + 0x13000, &resource_bcast); - resource_dlbu_found = _mali_osk_resource_find(global_gpu_base_address + 0x14000, &resource_dlbu); - resource_pp_mmu_bcast_found = _mali_osk_resource_find(global_gpu_base_address + 0x15000, &resource_pp_mmu_bcast); - resource_pp_bcast_found = _mali_osk_resource_find(global_gpu_base_address + 0x16000, &resource_pp_bcast); + resource_bcast_found = _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_BCAST, &resource_bcast); + resource_dlbu_found = _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_DLBU, &resource_dlbu); + resource_pp_mmu_bcast_found = _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_MMU_BCAST, &resource_pp_mmu_bcast); + resource_pp_bcast_found = _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_BCAST, &resource_pp_bcast); if (_MALI_OSK_ERR_OK != resource_bcast_found || _MALI_OSK_ERR_OK != resource_dlbu_found || @@ -644,12 +623,12 @@ static void mali_use_default_pm_domain_config(void) MALI_DEBUG_ASSERT(0 != global_gpu_base_address); /* GP core */ - if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x00000, NULL)) { + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_GP, NULL)) { mali_pmu_set_domain_mask(MALI_GP_DOMAIN_INDEX, 0x01); } /* PP0 - PP3 core */ - if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x08000, NULL)) { + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_0, NULL)) { ++pp_count_gr1; if (mali_is_mali400()) { @@ -659,7 +638,7 @@ static void mali_use_default_pm_domain_config(void) } } - if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x0A000, NULL)) { + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_1, NULL)) { ++pp_count_gr1; if (mali_is_mali400()) { @@ -669,7 +648,7 @@ static void mali_use_default_pm_domain_config(void) } } - if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x0C000, NULL)) { + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_2, NULL)) { ++pp_count_gr1; if (mali_is_mali400()) { @@ -679,7 +658,7 @@ static void mali_use_default_pm_domain_config(void) } } - if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x0E000, NULL)) { + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_3, NULL)) { ++pp_count_gr1; if (mali_is_mali400()) { @@ -690,32 +669,32 @@ static void mali_use_default_pm_domain_config(void) } /* PP4 - PP7 */ - if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x28000, NULL)) { + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_4, NULL)) { ++pp_count_gr2; mali_pmu_set_domain_mask(MALI_PP4_DOMAIN_INDEX, 0x01<<3); } - if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x2A000, NULL)) { + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_5, NULL)) { ++pp_count_gr2; mali_pmu_set_domain_mask(MALI_PP5_DOMAIN_INDEX, 0x01<<3); } - if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x2C000, NULL)) { + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_6, NULL)) { ++pp_count_gr2; mali_pmu_set_domain_mask(MALI_PP6_DOMAIN_INDEX, 0x01<<3); } - if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x2E000, NULL)) { + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PP_7, NULL)) { ++pp_count_gr2; mali_pmu_set_domain_mask(MALI_PP7_DOMAIN_INDEX, 0x01<<3); } /* L2gp/L2PP0/L2PP4 */ - if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x10000, NULL)) { + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_L2_PP_GRP0, NULL)) { ++l2_count; if (mali_is_mali400()) { @@ -725,13 +704,13 @@ static void mali_use_default_pm_domain_config(void) } } - if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x1000, NULL)) { + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_L2_GP, NULL)) { ++l2_count; mali_pmu_set_domain_mask(MALI_L21_DOMAIN_INDEX, 0x01<<1); } - if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x11000, NULL)) { + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_L2_PP_GRP1, NULL)) { ++l2_count; mali_pmu_set_domain_mask(MALI_L22_DOMAIN_INDEX, 0x01<<3); @@ -766,7 +745,7 @@ static _mali_osk_errcode_t mali_parse_config_pmu(void) MALI_DEBUG_ASSERT(0 != global_gpu_base_address); - if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x02000, &resource_pmu)) { + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_PMU, &resource_pmu)) { struct mali_pmu_core *pmu; mali_set_pmu_global_domain_config(); @@ -786,7 +765,7 @@ static _mali_osk_errcode_t mali_parse_config_dma(void) { _mali_osk_resource_t resource_dma; - if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(global_gpu_base_address + 0x12000, &resource_dma)) { + if (_MALI_OSK_ERR_OK == _mali_osk_resource_find_by_id(MALI_RESOURCE_INDEX_DMA, &resource_dma)) { if (NULL == mali_dma_create(&resource_dma)) { return _MALI_OSK_ERR_FAULT; } diff --git a/drivers/gpu/arm/mali/common/mali_osk_mali.h b/drivers/gpu/arm/mali/common/mali_osk_mali.h index 7ed701cf77a704..b9be82b545a970 100644 --- a/drivers/gpu/arm/mali/common/mali_osk_mali.h +++ b/drivers/gpu/arm/mali/common/mali_osk_mali.h @@ -65,6 +65,8 @@ struct _mali_osk_device_data { /* Fuction that platform callback for freq tunning, needed when MALI400_POWER_PERFORMANCE_POLICY enabled */ int (*set_freq_callback)(unsigned int mhz); + + struct mali_resource resource[MALI_RESOURCE_INDEX_LAST]; }; /** @brief Find Mali GPU HW resource @@ -74,6 +76,8 @@ struct _mali_osk_device_data { * @return _MALI_OSK_ERR_OK on success, _MALI_OSK_ERR_ITEM_NOT_FOUND if resource is not found */ _mali_osk_errcode_t _mali_osk_resource_find(u32 addr, _mali_osk_resource_t *res); +_mali_osk_errcode_t _mali_osk_resource_find_by_id(enum mali_resource_index, _mali_osk_resource_t *res); + /** @brief Find Mali GPU HW base address diff --git a/drivers/gpu/arm/mali/include/linux/mali/mali_utgard.h b/drivers/gpu/arm/mali/include/linux/mali/mali_utgard.h index b7a9133691b9f0..6bd14a8d4ebcde 100644 --- a/drivers/gpu/arm/mali/include/linux/mali/mali_utgard.h +++ b/drivers/gpu/arm/mali/include/linux/mali/mali_utgard.h @@ -340,6 +340,46 @@ struct mali_gpu_utilization_data { #endif }; +struct mali_resource +{ + const char * description; /**< short description of the resource */ + u32 base; /**< Physical base address of the resource, as seen by Mali resources. */ + u32 irq; /**< IRQ number delivered to the CPU, or -1 to tell the driver to probe for it (if possible) */ +}; + +enum mali_resource_index { + MALI_RESOURCE_INDEX_L2, + MALI_RESOURCE_INDEX_L2_GP, + MALI_RESOURCE_INDEX_L2_PP_GRP0, + MALI_RESOURCE_INDEX_L2_PP_GRP1, + MALI_RESOURCE_INDEX_GP, + MALI_RESOURCE_INDEX_GP_MMU, + MALI_RESOURCE_INDEX_PP_0, + MALI_RESOURCE_INDEX_PP_MMU_0, + MALI_RESOURCE_INDEX_PP_1, + MALI_RESOURCE_INDEX_PP_MMU_1, + MALI_RESOURCE_INDEX_PP_2, + MALI_RESOURCE_INDEX_PP_MMU_2, + MALI_RESOURCE_INDEX_PP_3, + MALI_RESOURCE_INDEX_PP_MMU_3, + MALI_RESOURCE_INDEX_PP_4, + MALI_RESOURCE_INDEX_PP_MMU_4, + MALI_RESOURCE_INDEX_PP_5, + MALI_RESOURCE_INDEX_PP_MMU_5, + MALI_RESOURCE_INDEX_PP_6, + MALI_RESOURCE_INDEX_PP_MMU_6, + MALI_RESOURCE_INDEX_PP_7, + MALI_RESOURCE_INDEX_PP_MMU_7, + MALI_RESOURCE_INDEX_PMU, + MALI_RESOURCE_INDEX_BCAST, + MALI_RESOURCE_INDEX_DLBU, + MALI_RESOURCE_INDEX_PP_BCAST, + MALI_RESOURCE_INDEX_PP_MMU_BCAST, + MALI_RESOURCE_INDEX_DMA, + + MALI_RESOURCE_INDEX_LAST, +}; + struct mali_gpu_device_data { /* Dedicated GPU memory range (physical). */ unsigned long dedicated_mem_start; diff --git a/drivers/gpu/arm/mali/linux/mali_kernel_linux.c b/drivers/gpu/arm/mali/linux/mali_kernel_linux.c index 9bf17de70550c8..4592d1c0dc4ad6 100644 --- a/drivers/gpu/arm/mali/linux/mali_kernel_linux.c +++ b/drivers/gpu/arm/mali/linux/mali_kernel_linux.c @@ -16,6 +16,7 @@ #include /* file system operations */ #include /* character device definitions */ #include /* memory manager definitions */ +#include #include #include #include @@ -27,6 +28,7 @@ #include "mali_session.h" #include "mali_kernel_core.h" #include "mali_osk.h" +#include "mali_osk_mali.h" #include "mali_kernel_linux.h" #include "mali_ukk.h" #include "mali_ukk_wrappers.h" @@ -117,6 +119,7 @@ static char mali_dev_name[] = "mali"; /* should be const, but the functions we c /* This driver only supports one Mali device, and this variable stores this single platform device */ struct platform_device *mali_platform_device = NULL; +struct _mali_osk_device_data *mali_platform_data = NULL; /* This driver only supports one Mali device, and this variable stores the exposed misc device (/dev/mali) */ static struct miscdevice mali_miscdevice = { 0, }; @@ -163,6 +166,13 @@ static const struct dev_pm_ops mali_dev_pm_ops = { .poweroff = mali_driver_suspend_scheduler, }; +#ifdef CONFIG_OF +static const struct of_device_id arm_mali_match[] = { + { .compatible = "arm,mali400-mp4" }, + {}, +}; +#endif + /* The Mali device driver struct */ static struct platform_driver mali_platform_driver = { .probe = mali_probe, @@ -173,6 +183,7 @@ static struct platform_driver mali_platform_driver = { .owner = THIS_MODULE, .bus = &platform_bus_type, .pm = &mali_dev_pm_ops, + .of_match_table = of_match_ptr(arm_mali_match), }, }; @@ -376,6 +387,140 @@ void mali_module_exit(void) MALI_PRINT(("Mali device driver unloaded\n")); } +#ifdef CONFIG_OF +static struct _mali_osk_device_data *mali_parse_dt(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + u32 tmp_interval, tmp_size; + struct _mali_osk_device_data* os_data = (struct _mali_osk_device_data*)mali_platform_data; + struct resource *res_mem; + enum mali_resource_index index; + + if (NULL != os_data) + return os_data; + + os_data = devm_kzalloc(&pdev->dev, sizeof(struct _mali_osk_device_data), GFP_KERNEL); + if (!os_data) + return NULL; + + mali_platform_data = os_data; + + if (of_property_read_u32(np, "utilization-interval", &tmp_interval)) + goto error_parse_dt; + if (of_property_read_u32(np, "shared-memory-size", &tmp_size)) + goto error_parse_dt; + + os_data->shared_mem_size = tmp_size; + //os_data->utilization_interval = tmp_interval; + //os_data->utilization_callback = mali_gpu_utilization_callback; + + index = MALI_RESOURCE_INDEX_L2; + res_mem = platform_get_resource_byname (pdev, IORESOURCE_MEM, "l2"); + if (res_mem) { + os_data->resource[index].description = res_mem->name ?: dev_name(&pdev->dev); + os_data->resource[index].base = res_mem->start; + } + + index = MALI_RESOURCE_INDEX_GP; + res_mem = platform_get_resource_byname (pdev, IORESOURCE_MEM, "gp"); + if (res_mem) { + os_data->resource[index].description = res_mem->name ?: dev_name(&pdev->dev); + os_data->resource[index].base = res_mem->start; + os_data->resource[index].irq = platform_get_irq_byname(pdev, "gp"); + } + + index = MALI_RESOURCE_INDEX_GP_MMU; + res_mem = platform_get_resource_byname (pdev, IORESOURCE_MEM, "gp_mmu"); + if (res_mem) { + os_data->resource[index].description = res_mem->name ?: dev_name(&pdev->dev); + os_data->resource[index].base = res_mem->start; + os_data->resource[index].irq = platform_get_irq_byname(pdev, "gp_mmu"); + } + + index = MALI_RESOURCE_INDEX_PP_0; + res_mem = platform_get_resource_byname (pdev, IORESOURCE_MEM, "pp_0"); + if (res_mem) { + os_data->resource[index].description = res_mem->name ?: dev_name(&pdev->dev); + os_data->resource[index].base = res_mem->start; + os_data->resource[index].irq = platform_get_irq_byname(pdev, "pp_0"); + } + + index = MALI_RESOURCE_INDEX_PP_1; + res_mem = platform_get_resource_byname (pdev, IORESOURCE_MEM, "pp_1"); + if (res_mem) { + os_data->resource[index].description = res_mem->name ?: dev_name(&pdev->dev); + os_data->resource[index].base = res_mem->start; + os_data->resource[index].irq = platform_get_irq_byname(pdev, "pp_1"); + } + + index = MALI_RESOURCE_INDEX_PP_2; + res_mem = platform_get_resource_byname (pdev, IORESOURCE_MEM, "pp_2"); + if (res_mem) { + os_data->resource[index].description = res_mem->name ?: dev_name(&pdev->dev); + os_data->resource[index].base = res_mem->start; + os_data->resource[index].irq = platform_get_irq_byname(pdev, "pp_2"); + } + + index = MALI_RESOURCE_INDEX_PP_3; + res_mem = platform_get_resource_byname (pdev, IORESOURCE_MEM, "pp_3"); + if (res_mem) { + os_data->resource[index].description = res_mem->name ?: dev_name(&pdev->dev); + os_data->resource[index].base = res_mem->start; + os_data->resource[index].irq = platform_get_irq_byname(pdev, "pp_3"); + } + + index = MALI_RESOURCE_INDEX_PP_MMU_0; + res_mem = platform_get_resource_byname (pdev, IORESOURCE_MEM, "pp_mmu_0"); + if (res_mem) { + os_data->resource[index].description = res_mem->name ?: dev_name(&pdev->dev); + os_data->resource[index].base = res_mem->start; + os_data->resource[index].irq = platform_get_irq_byname(pdev, "pp_mmu_0"); + } + + index = MALI_RESOURCE_INDEX_PP_MMU_1; + res_mem = platform_get_resource_byname (pdev, IORESOURCE_MEM, "pp_mmu_1"); + if (res_mem) { + os_data->resource[index].description = res_mem->name ?: dev_name(&pdev->dev); + os_data->resource[index].base = res_mem->start; + os_data->resource[index].irq = platform_get_irq_byname(pdev, "pp_mmu_1"); + } + + index = MALI_RESOURCE_INDEX_PP_MMU_2; + res_mem = platform_get_resource_byname (pdev, IORESOURCE_MEM, "pp_mmu_2"); + if (res_mem) { + os_data->resource[index].description = res_mem->name ?: dev_name(&pdev->dev); + os_data->resource[index].base = res_mem->start; + os_data->resource[index].irq = platform_get_irq_byname(pdev, "pp_mmu_2"); + } + + index = MALI_RESOURCE_INDEX_PP_MMU_3; + res_mem = platform_get_resource_byname (pdev, IORESOURCE_MEM, "pp_mmu_3"); + if (res_mem) { + os_data->resource[index].description = res_mem->name ?: dev_name(&pdev->dev); + os_data->resource[index].base = res_mem->start; + os_data->resource[index].irq = platform_get_irq_byname(pdev, "pp_mmu_3"); + } + + index = MALI_RESOURCE_INDEX_DMA; + res_mem = platform_get_resource_byname (pdev, IORESOURCE_MEM, "dma"); + if (res_mem) { + os_data->resource[index].description = res_mem->name ?: dev_name(&pdev->dev); + os_data->resource[index].base = res_mem->start; + } + + return os_data; + +error_parse_dt: + dev_err(&pdev->dev, "Parsing device tree data error.\n"); + return NULL; +} +#else +static struct _mali_osk_device_data mali_parse_dt (struct platform_device *pdev) +{ + return NULL; +} +#endif + static int mali_probe(struct platform_device *pdev) { int err; @@ -388,6 +533,8 @@ static int mali_probe(struct platform_device *pdev) return -EEXIST; } + if (pdev->dev.of_node) + mali_parse_dt(pdev); mali_platform_device = pdev; if (_MALI_OSK_ERR_OK == _mali_osk_wq_init()) { diff --git a/drivers/gpu/arm/mali/linux/mali_kernel_linux.h b/drivers/gpu/arm/mali/linux/mali_kernel_linux.h index cf8e2564c154cb..00db47eeb5763b 100644 --- a/drivers/gpu/arm/mali/linux/mali_kernel_linux.h +++ b/drivers/gpu/arm/mali/linux/mali_kernel_linux.h @@ -20,6 +20,7 @@ extern "C" { #include "mali_osk_types.h" extern struct platform_device *mali_platform_device; +extern struct _mali_osk_device_data *mali_platform_data; #if MALI_LICENSE_IS_GPL /* Defined in mali_osk_irq.h */ diff --git a/drivers/gpu/arm/mali/linux/mali_osk_mali.c b/drivers/gpu/arm/mali/linux/mali_osk_mali.c index 626c35fd0e11d5..96bbabc1a6b1b0 100644 --- a/drivers/gpu/arm/mali/linux/mali_osk_mali.c +++ b/drivers/gpu/arm/mali/linux/mali_osk_mali.c @@ -22,6 +22,34 @@ #include "mali_osk.h" /* kernel side OS functions */ #include "mali_kernel_linux.h" +_mali_osk_errcode_t _mali_osk_resource_find_by_id(enum mali_resource_index index, _mali_osk_resource_t *res) +{ + struct mali_resource *ures = NULL; + + if (NULL == mali_platform_data) { + /* Not connected to a device */ + return _MALI_OSK_ERR_ITEM_NOT_FOUND; + } + + ures = &mali_platform_data->resource[index]; + + if (0 != ures->base) { + if (NULL != res) { + res->base = ures->base; + res->description = ures->description; + + /* Any (optional) IRQ resource belonging to this resource will follow */ + if (0 < ures->irq) + res->irq = ures->irq; + else + res->irq = -1; + } + return _MALI_OSK_ERR_OK; + } + + return _MALI_OSK_ERR_ITEM_NOT_FOUND; +} + _mali_osk_errcode_t _mali_osk_resource_find(u32 addr, _mali_osk_resource_t *res) { int i; @@ -79,7 +107,10 @@ _mali_osk_errcode_t _mali_osk_device_data_get(struct _mali_osk_device_data *data if (NULL != mali_platform_device) { struct mali_gpu_device_data* os_data = NULL; - os_data = (struct mali_gpu_device_data*)mali_platform_device->dev.platform_data; + if (NULL != mali_platform_data) + os_data = (struct mali_gpu_device_data*)mali_platform_data; + else + os_data = (struct mali_gpu_device_data*)mali_platform_device->dev.platform_data; if (NULL != os_data) { /* Copy data from OS dependant struct to Mali neutral struct (identical!) */ data->dedicated_mem_start = os_data->dedicated_mem_start; From 5d6340ad4fdd8364e04bca575053dc00dfe87d88 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Mon, 3 Feb 2014 16:14:05 -0600 Subject: [PATCH 756/788] mali: odroid (pegasus-m400) platform integration --- drivers/gpu/arm/mali/Kbuild | 1 + .../gpu/arm/mali/common/mali_kernel_core.c | 4 + drivers/gpu/arm/mali/common/mali_pm.c | 12 + drivers/gpu/arm/mali/platform/mali_platform.h | 129 +++++++++ .../platform/pegasus-m400/mali_platform.c | 273 ++++++++++++++++++ 5 files changed, 419 insertions(+) create mode 100644 drivers/gpu/arm/mali/platform/mali_platform.h create mode 100644 drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c diff --git a/drivers/gpu/arm/mali/Kbuild b/drivers/gpu/arm/mali/Kbuild index 222bbeeb2c5e43..de1a1650cfdda4 100644 --- a/drivers/gpu/arm/mali/Kbuild +++ b/drivers/gpu/arm/mali/Kbuild @@ -103,6 +103,7 @@ mali-y += \ common/mali_pm_domain.o \ linux/mali_osk_pm.o \ linux/mali_pmu_power_up_down.o \ + platform/pegasus-m400/mali_platform.o \ __malidrv_build_info.o ifneq ($(MALI_PLATFORM_FILES),) diff --git a/drivers/gpu/arm/mali/common/mali_kernel_core.c b/drivers/gpu/arm/mali/common/mali_kernel_core.c index fee93550919d1d..8a28be66a8878e 100644 --- a/drivers/gpu/arm/mali/common/mali_kernel_core.c +++ b/drivers/gpu/arm/mali/common/mali_kernel_core.c @@ -35,6 +35,7 @@ #include "mali_timeline.h" #include "mali_soft_job.h" #include "mali_pm_domain.h" +#include "mali_platform.h" #if defined(CONFIG_MALI400_PROFILING) #include "mali_osk_profiling.h" #endif @@ -974,6 +975,8 @@ _mali_osk_errcode_t mali_initialize_subsystems(void) /* Allowing the system to be turned off */ _mali_osk_pm_dev_ref_dec(); + mali_platform_init(); + MALI_SUCCESS; /* all ok */ /* Error handling */ @@ -1038,6 +1041,7 @@ void mali_terminate_subsystems(void) MALI_DEBUG_PRINT(2, ("terminate_subsystems() called\n")); /* shut down subsystems in reverse order from startup */ + mali_platform_deinit(); /* We need the GPU to be powered up for the terminate sequence */ _mali_osk_pm_dev_ref_add(); diff --git a/drivers/gpu/arm/mali/common/mali_pm.c b/drivers/gpu/arm/mali/common/mali_pm.c index 7ec2b092c84160..e8429fd7455bfc 100644 --- a/drivers/gpu/arm/mali/common/mali_pm.c +++ b/drivers/gpu/arm/mali/common/mali_pm.c @@ -19,16 +19,28 @@ #include "mali_pm_domain.h" #include "mali_pmu.h" +#include + +extern struct platform_device *mali_platform_device; + static mali_bool mali_power_on = MALI_FALSE; _mali_osk_errcode_t mali_pm_initialize(void) { +#ifdef CONFIG_PM_RUNTIME + pm_runtime_set_autosuspend_delay(&(mali_platform_device->dev), 1000); + pm_runtime_use_autosuspend(&(mali_platform_device->dev)); + pm_runtime_enable(&(mali_platform_device->dev)); +#endif _mali_osk_pm_dev_enable(); return _MALI_OSK_ERR_OK; } void mali_pm_terminate(void) { +#ifdef CONFIG_PM_RUNTIME + pm_runtime_disable(&(mali_platform_device->dev)); +#endif mali_pm_domain_terminate(); _mali_osk_pm_dev_disable(); } diff --git a/drivers/gpu/arm/mali/platform/mali_platform.h b/drivers/gpu/arm/mali/platform/mali_platform.h new file mode 100644 index 00000000000000..9d31c73a18a063 --- /dev/null +++ b/drivers/gpu/arm/mali/platform/mali_platform.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_platform.h + * Platform specific Mali driver functions + */ + +#ifndef __MALI_PLATFORM_H__ +#define __MALI_PLATFORM_H__ + +#include "mali_osk.h" +#ifndef USING_MALI_PMM +/* @brief System power up/down cores that can be passed into mali_platform_powerdown/up() */ +#define MALI_PLATFORM_SYSTEM 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief description of power change reasons + */ +typedef enum mali_power_mode_tag +{ + MALI_POWER_MODE_ON, + MALI_POWER_MODE_LIGHT_SLEEP, + MALI_POWER_MODE_DEEP_SLEEP, +} mali_power_mode; + +/** @brief Platform specific setup and initialisation of MALI + * + * This is called from the entrypoint of the driver to initialize the platform + * + * @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error. + */ +_mali_osk_errcode_t mali_platform_init(void); + +/** @brief Platform specific deinitialisation of MALI + * + * This is called on the exit of the driver to terminate the platform + * + * @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error. + */ +_mali_osk_errcode_t mali_platform_deinit(void); + +/** @brief Platform specific powerdown sequence of MALI + * + * Call as part of platform init if there is no PMM support, else the + * PMM will call it. + * There are three power modes defined: + * 1) MALI_POWER_MODE_ON + * 2) MALI_POWER_MODE_LIGHT_SLEEP + * 3) MALI_POWER_MODE_DEEP_SLEEP + * MALI power management module transitions to MALI_POWER_MODE_LIGHT_SLEEP mode when MALI is idle + * for idle timer (software timer defined in mali_pmm_policy_jobcontrol.h) duration, MALI transitions + * to MALI_POWER_MODE_LIGHT_SLEEP mode during timeout if there are no more jobs queued. + * MALI power management module transitions to MALI_POWER_MODE_DEEP_SLEEP mode when OS does system power + * off. + * Customer has to add power down code when MALI transitions to MALI_POWER_MODE_LIGHT_SLEEP or MALI_POWER_MODE_DEEP_SLEEP + * mode. + * MALI_POWER_MODE_ON mode is entered when the MALI is to powered up. Some customers want to control voltage regulators during + * the whole system powers on/off. Customer can track in this function whether the MALI is powered up from + * MALI_POWER_MODE_LIGHT_SLEEP or MALI_POWER_MODE_DEEP_SLEEP mode and manage the voltage regulators as well. + * @param power_mode defines the power modes + * @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error. + */ +_mali_osk_errcode_t mali_platform_power_mode_change(mali_power_mode power_mode); + + +/** @brief Platform specific handling of GPU utilization data + * + * When GPU utilization data is enabled, this function will be + * periodically called. + * + * @param utilization The workload utilization of the Mali GPU. 0 = no utilization, 256 = full utilization. + */ +void mali_gpu_utilization_handler(u32 utilization); + +/** @brief Setting the power domain of MALI + * + * This function sets the power domain of MALI if Linux run time power management is enabled + * + * @param dev Reference to struct platform_device (defined in linux) used by MALI GPU + */ +void mali_utilization_suspend(void); + +#ifdef CONFIG_REGULATOR +int mali_regulator_get_usecount(void); +void mali_regulator_disable(void); +void mali_regulator_enable(void); +void mali_regulator_set_voltage(int min_uV, int max_uV); +#endif + +mali_bool mali_clk_set_rate(unsigned int clk, unsigned int mhz); +unsigned long mali_clk_get_rate(void); +void mali_clk_put(mali_bool binc_mali_clk); + +#ifdef MALI_PMM_RUNTIME_JOB_CONTROL_ON +_mali_osk_errcode_t mali_platform_powerdown(u32 cores); +_mali_osk_errcode_t mali_platform_powerup(u32 cores); +#endif + +#ifdef CONFIG_MALI_DVFS +#define MALI_DVFS_STEPS 5 +mali_bool init_mali_dvfs_status(int step); +void deinit_mali_dvfs_status(void); +mali_bool mali_dvfs_handler(u32 utilization); +int mali_dvfs_is_running(void); +void mali_dvfs_late_resume(void); +int get_mali_dvfs_control_status(void); +mali_bool set_mali_dvfs_current_step(unsigned int step); +void mali_default_step_set(int step, mali_bool boostup); +int change_dvfs_tableset(int change_clk, int change_step); +#endif + +void mali_set_runtime_resume_params(int clk, int volt); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c b/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c new file mode 100644 index 00000000000000..b6c2c1a74e07fc --- /dev/null +++ b/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2010 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_platform.c + * Platform specific Mali driver functions for a default platform + */ +#include +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_platform.h" + +#ifdef USING_MALI_PMM +#include "mali_pmm.h" +#endif + +#include +#include +#include +#include +#include + +#include + +#define CLK_DIV_STAT_G3D 0x1003C62C +#define CLK_DESC "clk-divider-status" + +typedef struct mali_runtime_resumeTag{ + int clk; + int vol; +} mali_runtime_resume_table; + +mali_runtime_resume_table mali_runtime_resume = {400, 1100000}; + +static struct clk *sclk_g3d_clock = NULL; + +/* Please take special care lowering these values, specially the voltage + * as it can cause system stability problems: random oops, usb hub resets */ +int mali_gpu_clk = 533; /* 533 MHz */ +int mali_gpu_vol = 1125000;/* 1.1125 V */ + +#ifdef CONFIG_MALI_DVFS +#define MALI_DVFS_DEFAULT_STEP 0 +#endif + +int gpu_power_state; +static int bPoweroff; + +#ifdef CONFIG_REGULATOR +struct regulator { + struct device *dev; + struct list_head list; + unsigned int always_on:1; + int uA_load; + int min_uV; + int max_uV; + char *supply_name; + struct device_attribute dev_attr; + struct regulator_dev *rdev; + struct dentry *debugfs; +}; +struct regulator *g3d_regulator = NULL; +#endif + + +mali_io_address clk_register_map = 0; +_mali_osk_mutex_t *mali_dvfs_lock = 0; + +void mali_set_runtime_resume_params(int clk, int volt) +{ + mali_runtime_resume.clk = clk; + mali_runtime_resume.vol = volt; +} + +#ifdef CONFIG_REGULATOR +int mali_regulator_get_usecount(void) +{ + struct regulator_dev *rdev; + + if( IS_ERR_OR_NULL(g3d_regulator) ) + { + MALI_DEBUG_PRINT(1, ("error on mali_regulator_get_usecount : g3d_regulator is null\n")); + return 0; + } + rdev = g3d_regulator->rdev; + return rdev->use_count; +} + +void mali_regulator_disable(void) +{ + bPoweroff = 1; + if( IS_ERR_OR_NULL(g3d_regulator) ) + { + MALI_DEBUG_PRINT(1, ("error on mali_regulator_disable : g3d_regulator is null\n")); + return; + } + regulator_disable(g3d_regulator); + MALI_DEBUG_PRINT(1, ("regulator_disable -> use cnt: %d \n",mali_regulator_get_usecount())); +} + +void mali_regulator_enable(void) +{ + bPoweroff = 0; + if( IS_ERR_OR_NULL(g3d_regulator) ) + { + MALI_DEBUG_PRINT(1, ("error on mali_regulator_enable : g3d_regulator is null\n")); + return; + } + regulator_enable(g3d_regulator); + MALI_DEBUG_PRINT(1, ("regulator_enable -> use cnt: %d \n",mali_regulator_get_usecount())); +} + +void mali_regulator_set_voltage(int min_uV, int max_uV) +{ + int voltage; +#ifndef CONFIG_MALI_DVFS + min_uV = mali_gpu_vol; + max_uV = mali_gpu_vol; +#endif + + _mali_osk_mutex_wait(mali_dvfs_lock); + + if( IS_ERR_OR_NULL(g3d_regulator) ) + { + MALI_DEBUG_PRINT(1, ("error on mali_regulator_set_voltage : g3d_regulator is null\n")); + return; + } + MALI_DEBUG_PRINT(2, ("= regulator_set_voltage: %d, %d \n",min_uV, max_uV)); + regulator_set_voltage(g3d_regulator,min_uV,max_uV); + voltage = regulator_get_voltage(g3d_regulator); + mali_gpu_vol = voltage; + MALI_DEBUG_PRINT(1, ("= regulator_get_voltage: %d \n",mali_gpu_vol)); + + _mali_osk_mutex_signal(mali_dvfs_lock); +} +#endif + +mali_bool mali_clk_enable(void) +{ + struct device *dev = &mali_platform_device->dev; + unsigned long rate; + + sclk_g3d_clock = clk_get(dev, "sclk_g3d"); + if (IS_ERR(sclk_g3d_clock)) { + MALI_PRINT( ("MALI Error : failed to get source mali clock\n")); + return MALI_FALSE; + } + + _mali_osk_mutex_wait(mali_dvfs_lock); + + if (clk_prepare_enable(sclk_g3d_clock) < 0) + { + printk("~~~~~~~~ERROR: [%s] %d\n ",__func__,__LINE__); + return MALI_FALSE; + } + + rate = clk_get_rate(sclk_g3d_clock); + mali_gpu_clk = (int)(rate / 1000000); + + MALI_DEBUG_PRINT(2,("= clk_get_rate: %d \n",mali_gpu_clk)); + + _mali_osk_mutex_signal(mali_dvfs_lock); + + return MALI_TRUE; +} + +static mali_bool init_mali_clock(void) +{ + static mali_bool initialized = MALI_FALSE; + mali_bool ret = MALI_TRUE; + + gpu_power_state = 0; + + if (initialized) + return ret; // already initialized + + mali_dvfs_lock = _mali_osk_mutex_init(0, 0); + if (mali_dvfs_lock == NULL) + return _MALI_OSK_ERR_FAULT; + + if (mali_clk_enable() == MALI_FALSE) + return MALI_FALSE; + + MALI_PRINT(("init_mali_clock\n")); + + +#ifdef CONFIG_REGULATOR +#ifdef USING_MALI_PMM + g3d_regulator = regulator_get(&mali_platform_device.dev, "vdd_g3d"); +#else + g3d_regulator = regulator_get(NULL, "vdd_g3d"); +#endif + + if (IS_ERR(g3d_regulator)) + { + MALI_PRINT( ("MALI Error : failed to get vdd_g3d\n")); + ret = MALI_FALSE; + goto err_regulator; + } + + regulator_enable(g3d_regulator); + MALI_DEBUG_PRINT(1, ("= regulator_enable -> use cnt: %d \n",mali_regulator_get_usecount())); + mali_regulator_set_voltage(mali_gpu_vol, mali_gpu_vol); +#endif + + MALI_DEBUG_PRINT(2, ("MALI Clock is set at mali driver\n")); + initialized = MALI_TRUE; + return MALI_TRUE; + +#ifdef CONFIG_REGULATOR +err_regulator: + regulator_put(g3d_regulator); +#endif + return ret; +} + +_mali_osk_errcode_t mali_platform_init() +{ + MALI_CHECK(init_mali_clock(), _MALI_OSK_ERR_FAULT); +#ifdef CONFIG_MALI_DVFS + if (!clk_register_map) clk_register_map = _mali_osk_mem_mapioregion( CLK_DIV_STAT_G3D, 0x20, CLK_DESC ); + if(!init_mali_dvfs_status(MALI_DVFS_DEFAULT_STEP)) + MALI_DEBUG_PRINT(1, ("mali_platform_init failed\n")); +#endif + + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_platform_deinit() +{ + if (sclk_g3d_clock) { + clk_disable_unprepare(sclk_g3d_clock); + clk_put(sclk_g3d_clock); + sclk_g3d_clock = NULL; + } + +#ifdef CONFIG_REGULATOR + if (g3d_regulator) + { + regulator_put(g3d_regulator); + g3d_regulator=NULL; + } +#endif + +#ifdef CONFIG_MALI_DVFS + deinit_mali_dvfs_status(); + if (clk_register_map ) + { + _mali_osk_mem_unmapioregion(CLK_DIV_STAT_G3D, 0x20, clk_register_map); + clk_register_map=0; + } +#endif + + MALI_SUCCESS; +} + +void mali_gpu_utilization_handler(u32 utilization) +{ + if (bPoweroff==0) + { +#ifdef CONFIG_MALI_DVFS + if(!mali_dvfs_handler(utilization)) + MALI_DEBUG_PRINT(1,( "error on mali dvfs status in utilization\n")); +#endif + } +} From b633b5495e3806ff246f67a2e62a62cc176483cf Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Wed, 9 Jul 2014 11:56:57 +0100 Subject: [PATCH 757/788] ARM: dts: exynos4412-odroid: add Mali devicetree entry Also adjust the voltage range of the G3D regulator. --- .../boot/dts/exynos4412-odroid-common.dtsi | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index 0d67340f3e9858..1a78edb8682239 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -510,8 +510,8 @@ buck4_reg: buck@4 { regulator-compatible = "BUCK4"; regulator-name = "vdd_g3d"; - regulator-min-microvolt = <850000>; - regulator-max-microvolt = <1200000>; + regulator-min-microvolt = <950000>; + regulator-max-microvolt = <1150000>; regulator-microvolt-offset = <50000>; BUCK4-supply = <®_sysvdd>; }; @@ -655,6 +655,42 @@ memory-region = <&mfc_left>, <&mfc_right>; memory-region-names = "left", "right"; }; + + mali@13000000 { + compatible = "arm,mali400-mp4"; + #address-cells = <1>; + #size-cells = <1>; + + shared-memory-size = <0x10000000>; + + /* mali dvfs */ + utilization-interval = <1000>; + + samsung,power-domain = <&pd_g3d>; + status = "okay"; + + /* Propagate VPLL output clock to SCLK_G3D and ensure that the DIV_G3D + * divider is 1. */ + assigned-clocks = <&clock CLK_MOUT_G3D1>, <&clock CLK_MOUT_G3D>, <&clock CLK_FOUT_VPLL>, <&clock CLK_SCLK_G3D>; + assigned-clock-parents = <&clock CLK_SCLK_VPLL>, <&clock CLK_MOUT_G3D1>; + assigned-clock-rates = <0>, <0>, <533000000>, <533000000>; + + clocks = <&clock CLK_SCLK_G3D>; + clock-names = "sclk_g3d"; + + reg = <0x13001000 0x200>, <0x13000000 0x100>, <0x13003000 0x100>, + <0x13008000 0x1100>, <0x13004000 0x100>, <0x1300A000 0x1100>, + <0x13005000 0x100>, <0x1300C000 0x1100>, <0x13006000 0x100>, + <0x1300E000 0x1100>, <0x13007000 0x100>, <0x13012000 0x100>; + reg-names = "l2", "gp", "gp_mmu", "pp_0", "pp_mmu_0", "pp_1", "pp_mmu_1", + "pp_2", "pp_mmu_2", "pp_3", "pp_mmu_3", "dma"; + + interrupts = <0 127 0>, <0 122 0>, <0 123 0>, <0 118 0>, <0 124 0>, + <0 119 0>, <0 125 0>, <0 120 0>, <0 126 0>, <0 121 0>; + interrupt-names = "gp", "gp_mmu", "pp_0", "pp_mmu_0", "pp_1", "pp_mmu_1", + "pp_2", "pp_mmu_2", "pp_3", "pp_mmu_3"; + + }; }; &ppmu_dmc0 { From b77bbc0b824120368f205870ebdab45586edda19 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Tue, 3 Feb 2015 17:54:41 +0100 Subject: [PATCH 758/788] mali: call mali_platform_init sooner Mali GPU detection needs to access certain Mali registers, which fails when regulator and clocks are disabled. Fix this by calling mali_platform_init earlier in the initialization procedure. Based on a patch by Alban Browaeys . --- drivers/gpu/arm/mali/common/mali_kernel_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/arm/mali/common/mali_kernel_core.c b/drivers/gpu/arm/mali/common/mali_kernel_core.c index 8a28be66a8878e..9256feaeb473fd 100644 --- a/drivers/gpu/arm/mali/common/mali_kernel_core.c +++ b/drivers/gpu/arm/mali/common/mali_kernel_core.c @@ -929,6 +929,8 @@ _mali_osk_errcode_t mali_initialize_subsystems(void) if (_MALI_OSK_ERR_OK != err) goto pmu_reset_failed; } + mali_platform_init(); + /* Detect which Mali GPU we are dealing with */ err = mali_parse_product_info(); if (_MALI_OSK_ERR_OK != err) goto product_info_parsing_failed; @@ -975,8 +977,6 @@ _mali_osk_errcode_t mali_initialize_subsystems(void) /* Allowing the system to be turned off */ _mali_osk_pm_dev_ref_dec(); - mali_platform_init(); - MALI_SUCCESS; /* all ok */ /* Error handling */ From 9b120e1c7c12c41db6193fb6858f71aa9825d2f1 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Wed, 4 Feb 2015 16:33:17 +0100 Subject: [PATCH 759/788] mali: use generic power domain bindings The generic bindings are now used to link to the G3D PD. --- arch/arm/boot/dts/exynos4412-odroid-common.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index 1a78edb8682239..15e574dcddd2b6 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -666,7 +666,7 @@ /* mali dvfs */ utilization-interval = <1000>; - samsung,power-domain = <&pd_g3d>; + power-domains = <&pd_g3d>; status = "okay"; /* Propagate VPLL output clock to SCLK_G3D and ensure that the DIV_G3D From 4187fbde931c45bbee53687bcdecccf6866353e7 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Wed, 4 Feb 2015 17:57:46 +0100 Subject: [PATCH 760/788] mali: replace CONFIG_PM_RUNTIME with CONFIG_PM The old option was removed from upstream. --- drivers/gpu/arm/mali/Kbuild | 2 +- drivers/gpu/arm/mali/common/mali_pm.c | 4 ++-- drivers/gpu/arm/mali/linux/mali_kernel_linux.c | 6 +++--- drivers/gpu/arm/mali/linux/mali_osk_pm.c | 14 +++++++------- drivers/gpu/arm/mali/platform/arm/arm.c | 4 ++-- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/arm/mali/Kbuild b/drivers/gpu/arm/mali/Kbuild index de1a1650cfdda4..c000dbf22a6374 100644 --- a/drivers/gpu/arm/mali/Kbuild +++ b/drivers/gpu/arm/mali/Kbuild @@ -30,7 +30,7 @@ ifeq ($(wildcard $(src)/linux/license/gpl/*),) ifeq ($(CONFIG_MALI400_PROFILING),y) $(error Profiling is incompatible with non-GPL license) endif - ifeq ($(CONFIG_PM_RUNTIME),y) + ifeq ($(CONFIG_PM),y) $(error Runtime PM is incompatible with non-GPL license) endif ifeq ($(CONFIG_DMA_SHARED_BUFFER),y) diff --git a/drivers/gpu/arm/mali/common/mali_pm.c b/drivers/gpu/arm/mali/common/mali_pm.c index e8429fd7455bfc..fc338837336105 100644 --- a/drivers/gpu/arm/mali/common/mali_pm.c +++ b/drivers/gpu/arm/mali/common/mali_pm.c @@ -27,7 +27,7 @@ static mali_bool mali_power_on = MALI_FALSE; _mali_osk_errcode_t mali_pm_initialize(void) { -#ifdef CONFIG_PM_RUNTIME +#ifdef CONFIG_PM pm_runtime_set_autosuspend_delay(&(mali_platform_device->dev), 1000); pm_runtime_use_autosuspend(&(mali_platform_device->dev)); pm_runtime_enable(&(mali_platform_device->dev)); @@ -38,7 +38,7 @@ _mali_osk_errcode_t mali_pm_initialize(void) void mali_pm_terminate(void) { -#ifdef CONFIG_PM_RUNTIME +#ifdef CONFIG_PM pm_runtime_disable(&(mali_platform_device->dev)); #endif mali_pm_domain_terminate(); diff --git a/drivers/gpu/arm/mali/linux/mali_kernel_linux.c b/drivers/gpu/arm/mali/linux/mali_kernel_linux.c index 4592d1c0dc4ad6..1a0684ea593793 100644 --- a/drivers/gpu/arm/mali/linux/mali_kernel_linux.c +++ b/drivers/gpu/arm/mali/linux/mali_kernel_linux.c @@ -141,7 +141,7 @@ static int mali_remove(struct platform_device *pdev); static int mali_driver_suspend_scheduler(struct device *dev); static int mali_driver_resume_scheduler(struct device *dev); -#ifdef CONFIG_PM_RUNTIME +#ifdef CONFIG_PM static int mali_driver_runtime_suspend(struct device *dev); static int mali_driver_runtime_resume(struct device *dev); static int mali_driver_runtime_idle(struct device *dev); @@ -154,7 +154,7 @@ extern int mali_platform_device_unregister(void); /* Linux power management operations provided by the Mali device driver */ static const struct dev_pm_ops mali_dev_pm_ops = { -#ifdef CONFIG_PM_RUNTIME +#ifdef CONFIG_PM .runtime_suspend = mali_driver_runtime_suspend, .runtime_resume = mali_driver_runtime_resume, .runtime_idle = mali_driver_runtime_idle, @@ -611,7 +611,7 @@ static int mali_driver_resume_scheduler(struct device *dev) return 0; } -#ifdef CONFIG_PM_RUNTIME +#ifdef CONFIG_PM static int mali_driver_runtime_suspend(struct device *dev) { mali_pm_runtime_suspend(); diff --git a/drivers/gpu/arm/mali/linux/mali_osk_pm.c b/drivers/gpu/arm/mali/linux/mali_osk_pm.c index b7f224dd8bc7e7..4a5b1f8f197fc3 100644 --- a/drivers/gpu/arm/mali/linux/mali_osk_pm.c +++ b/drivers/gpu/arm/mali/linux/mali_osk_pm.c @@ -15,9 +15,9 @@ #include -#ifdef CONFIG_PM_RUNTIME +#ifdef CONFIG_PM #include -#endif /* CONFIG_PM_RUNTIME */ +#endif /* CONFIG_PM */ #include #include #include "mali_osk.h" @@ -39,7 +39,7 @@ void _mali_osk_pm_dev_disable(void) /* Can NOT run in atomic context */ _mali_osk_errcode_t _mali_osk_pm_dev_ref_add(void) { -#ifdef CONFIG_PM_RUNTIME +#ifdef CONFIG_PM int err; MALI_DEBUG_ASSERT_POINTER(mali_platform_device); err = pm_runtime_get_sync(&(mali_platform_device->dev)); @@ -57,7 +57,7 @@ _mali_osk_errcode_t _mali_osk_pm_dev_ref_add(void) /* Can run in atomic context */ void _mali_osk_pm_dev_ref_dec(void) { -#ifdef CONFIG_PM_RUNTIME +#ifdef CONFIG_PM MALI_DEBUG_ASSERT_POINTER(mali_platform_device); _mali_osk_atomic_dec(&mali_pm_ref_count); pm_runtime_mark_last_busy(&(mali_platform_device->dev)); @@ -69,7 +69,7 @@ void _mali_osk_pm_dev_ref_dec(void) /* Can run in atomic context */ mali_bool _mali_osk_pm_dev_ref_add_no_power_on(void) { -#ifdef CONFIG_PM_RUNTIME +#ifdef CONFIG_PM u32 ref; MALI_DEBUG_ASSERT_POINTER(mali_platform_device); pm_runtime_get_noresume(&(mali_platform_device->dev)); @@ -84,7 +84,7 @@ mali_bool _mali_osk_pm_dev_ref_add_no_power_on(void) /* Can run in atomic context */ void _mali_osk_pm_dev_ref_dec_no_power_on(void) { -#ifdef CONFIG_PM_RUNTIME +#ifdef CONFIG_PM MALI_DEBUG_ASSERT_POINTER(mali_platform_device); pm_runtime_put_autosuspend(&(mali_platform_device->dev)); MALI_DEBUG_PRINT(4, ("Mali OSK PM: No-power ref released (%u)\n", _mali_osk_atomic_read(&mali_pm_ref_count))); @@ -93,7 +93,7 @@ void _mali_osk_pm_dev_ref_dec_no_power_on(void) void _mali_osk_pm_dev_barrier(void) { -#ifdef CONFIG_PM_RUNTIME +#ifdef CONFIG_PM pm_runtime_barrier(&(mali_platform_device->dev)); #endif } diff --git a/drivers/gpu/arm/mali/platform/arm/arm.c b/drivers/gpu/arm/mali/platform/arm/arm.c index a3da4c0e10fa47..0b5bb8902213cc 100644 --- a/drivers/gpu/arm/mali/platform/arm/arm.c +++ b/drivers/gpu/arm/mali/platform/arm/arm.c @@ -17,7 +17,7 @@ #include #include #include -#ifdef CONFIG_PM_RUNTIME +#ifdef CONFIG_PM #include #endif #include @@ -138,7 +138,7 @@ int mali_platform_device_register(void) /* Register the platform device */ err = platform_device_register(&mali_gpu_device); if (0 == err) { -#ifdef CONFIG_PM_RUNTIME +#ifdef CONFIG_PM pm_runtime_set_autosuspend_delay(&(mali_gpu_device.dev), 1000); pm_runtime_use_autosuspend(&(mali_gpu_device.dev)); pm_runtime_enable(&(mali_gpu_device.dev)); From c4f50055aef59ca3393acbace94f40b77dc7e577 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Wed, 4 Feb 2015 18:59:05 +0100 Subject: [PATCH 761/788] mali: start clean-up of odroid platform source --- drivers/gpu/arm/mali/platform/mali_platform.h | 2 +- .../platform/pegasus-m400/mali_platform.c | 66 ++++++++----------- 2 files changed, 30 insertions(+), 38 deletions(-) diff --git a/drivers/gpu/arm/mali/platform/mali_platform.h b/drivers/gpu/arm/mali/platform/mali_platform.h index 9d31c73a18a063..de0a268e29bf88 100644 --- a/drivers/gpu/arm/mali/platform/mali_platform.h +++ b/drivers/gpu/arm/mali/platform/mali_platform.h @@ -93,7 +93,7 @@ void mali_gpu_utilization_handler(u32 utilization); void mali_utilization_suspend(void); #ifdef CONFIG_REGULATOR -int mali_regulator_get_usecount(void); +unsigned int mali_regulator_get_usecount(void); void mali_regulator_disable(void); void mali_regulator_enable(void); void mali_regulator_set_voltage(int min_uV, int max_uV); diff --git a/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c b/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c index b6c2c1a74e07fc..224cf7db558789 100644 --- a/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c +++ b/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c @@ -32,7 +32,7 @@ #define CLK_DIV_STAT_G3D 0x1003C62C #define CLK_DESC "clk-divider-status" -typedef struct mali_runtime_resumeTag{ +typedef struct mali_runtime_resumeTag { int clk; int vol; } mali_runtime_resume_table; @@ -44,13 +44,13 @@ static struct clk *sclk_g3d_clock = NULL; /* Please take special care lowering these values, specially the voltage * as it can cause system stability problems: random oops, usb hub resets */ int mali_gpu_clk = 533; /* 533 MHz */ -int mali_gpu_vol = 1125000;/* 1.1125 V */ +int mali_gpu_vol = 1125000; /* 1.1125 V */ #ifdef CONFIG_MALI_DVFS #define MALI_DVFS_DEFAULT_STEP 0 #endif -int gpu_power_state; +int gpu_power_state; static int bPoweroff; #ifdef CONFIG_REGULATOR @@ -80,13 +80,12 @@ void mali_set_runtime_resume_params(int clk, int volt) } #ifdef CONFIG_REGULATOR -int mali_regulator_get_usecount(void) +unsigned int mali_regulator_get_usecount(void) { struct regulator_dev *rdev; - if( IS_ERR_OR_NULL(g3d_regulator) ) - { - MALI_DEBUG_PRINT(1, ("error on mali_regulator_get_usecount : g3d_regulator is null\n")); + if ( IS_ERR_OR_NULL(g3d_regulator) ) { + MALI_DEBUG_PRINT(1, ("Mali platform: getting regulator use count failed\n") ); return 0; } rdev = g3d_regulator->rdev; @@ -96,8 +95,7 @@ int mali_regulator_get_usecount(void) void mali_regulator_disable(void) { bPoweroff = 1; - if( IS_ERR_OR_NULL(g3d_regulator) ) - { + if ( IS_ERR_OR_NULL(g3d_regulator) ) { MALI_DEBUG_PRINT(1, ("error on mali_regulator_disable : g3d_regulator is null\n")); return; } @@ -127,44 +125,41 @@ void mali_regulator_set_voltage(int min_uV, int max_uV) _mali_osk_mutex_wait(mali_dvfs_lock); - if( IS_ERR_OR_NULL(g3d_regulator) ) - { + if( IS_ERR_OR_NULL(g3d_regulator) ) { MALI_DEBUG_PRINT(1, ("error on mali_regulator_set_voltage : g3d_regulator is null\n")); return; } - MALI_DEBUG_PRINT(2, ("= regulator_set_voltage: %d, %d \n",min_uV, max_uV)); - regulator_set_voltage(g3d_regulator,min_uV,max_uV); + MALI_DEBUG_PRINT(2, ("= regulator_set_voltage: %d, %d \n", min_uV, max_uV)); + regulator_set_voltage(g3d_regulator, min_uV, max_uV); voltage = regulator_get_voltage(g3d_regulator); mali_gpu_vol = voltage; - MALI_DEBUG_PRINT(1, ("= regulator_get_voltage: %d \n",mali_gpu_vol)); + MALI_DEBUG_PRINT(1, ("= regulator_get_voltage: %d \n", mali_gpu_vol)); _mali_osk_mutex_signal(mali_dvfs_lock); } #endif -mali_bool mali_clk_enable(void) +static mali_bool mali_clk_enable(void) { struct device *dev = &mali_platform_device->dev; unsigned long rate; sclk_g3d_clock = clk_get(dev, "sclk_g3d"); if (IS_ERR(sclk_g3d_clock)) { - MALI_PRINT( ("MALI Error : failed to get source mali clock\n")); + MALI_PRINT_ERROR(("Mali platform: failed to get source g3d clock\n")); return MALI_FALSE; } _mali_osk_mutex_wait(mali_dvfs_lock); - if (clk_prepare_enable(sclk_g3d_clock) < 0) - { - printk("~~~~~~~~ERROR: [%s] %d\n ",__func__,__LINE__); + if (clk_prepare_enable(sclk_g3d_clock) < 0) { + MALI_PRINT_ERROR(("Mali platform: failed to enable source g3d clock\n")); return MALI_FALSE; } rate = clk_get_rate(sclk_g3d_clock); - mali_gpu_clk = (int)(rate / 1000000); - MALI_DEBUG_PRINT(2,("= clk_get_rate: %d \n",mali_gpu_clk)); + MALI_PRINT(("Mali platform: g3d clock rate = %u MHz\n", rate / 1000000)); _mali_osk_mutex_signal(mali_dvfs_lock); @@ -190,7 +185,6 @@ static mali_bool init_mali_clock(void) MALI_PRINT(("init_mali_clock\n")); - #ifdef CONFIG_REGULATOR #ifdef USING_MALI_PMM g3d_regulator = regulator_get(&mali_platform_device.dev, "vdd_g3d"); @@ -198,15 +192,14 @@ static mali_bool init_mali_clock(void) g3d_regulator = regulator_get(NULL, "vdd_g3d"); #endif - if (IS_ERR(g3d_regulator)) - { - MALI_PRINT( ("MALI Error : failed to get vdd_g3d\n")); + if (IS_ERR(g3d_regulator)) { + MALI_DEBUG_PRINT(1, ("Mali platform: failed to get g3d regulator\n") ); ret = MALI_FALSE; goto err_regulator; } regulator_enable(g3d_regulator); - MALI_DEBUG_PRINT(1, ("= regulator_enable -> use cnt: %d \n",mali_regulator_get_usecount())); + MALI_DEBUG_PRINT(3, ("Mali platform: g3d regulator enabled (use count = %u)\n", mali_regulator_get_usecount())); mali_regulator_set_voltage(mali_gpu_vol, mali_gpu_vol); #endif @@ -225,8 +218,10 @@ _mali_osk_errcode_t mali_platform_init() { MALI_CHECK(init_mali_clock(), _MALI_OSK_ERR_FAULT); #ifdef CONFIG_MALI_DVFS - if (!clk_register_map) clk_register_map = _mali_osk_mem_mapioregion( CLK_DIV_STAT_G3D, 0x20, CLK_DESC ); - if(!init_mali_dvfs_status(MALI_DVFS_DEFAULT_STEP)) + if (!clk_register_map) + clk_register_map = _mali_osk_mem_mapioregion(CLK_DIV_STAT_G3D, 0x20, CLK_DESC); + + if (!init_mali_dvfs_status(MALI_DVFS_DEFAULT_STEP)) MALI_DEBUG_PRINT(1, ("mali_platform_init failed\n")); #endif @@ -242,19 +237,17 @@ _mali_osk_errcode_t mali_platform_deinit() } #ifdef CONFIG_REGULATOR - if (g3d_regulator) - { + if (g3d_regulator) { regulator_put(g3d_regulator); - g3d_regulator=NULL; + g3d_regulator = NULL; } #endif #ifdef CONFIG_MALI_DVFS deinit_mali_dvfs_status(); - if (clk_register_map ) - { + if (clk_register_map) { _mali_osk_mem_unmapioregion(CLK_DIV_STAT_G3D, 0x20, clk_register_map); - clk_register_map=0; + clk_register_map = 0; } #endif @@ -263,10 +256,9 @@ _mali_osk_errcode_t mali_platform_deinit() void mali_gpu_utilization_handler(u32 utilization) { - if (bPoweroff==0) - { + if (bPoweroff == 0) { #ifdef CONFIG_MALI_DVFS - if(!mali_dvfs_handler(utilization)) + if (!mali_dvfs_handler(utilization)) MALI_DEBUG_PRINT(1,( "error on mali dvfs status in utilization\n")); #endif } From d748d52791382a5815425ebde4df8a7a50b1d5b9 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Wed, 4 Feb 2015 19:00:38 +0100 Subject: [PATCH 762/788] mali: remove unused code from platform header These two defines are not found anywhere in the Mali source. --- drivers/gpu/arm/mali/platform/mali_platform.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/gpu/arm/mali/platform/mali_platform.h b/drivers/gpu/arm/mali/platform/mali_platform.h index de0a268e29bf88..5889fc97f65436 100644 --- a/drivers/gpu/arm/mali/platform/mali_platform.h +++ b/drivers/gpu/arm/mali/platform/mali_platform.h @@ -17,10 +17,6 @@ #define __MALI_PLATFORM_H__ #include "mali_osk.h" -#ifndef USING_MALI_PMM -/* @brief System power up/down cores that can be passed into mali_platform_powerdown/up() */ -#define MALI_PLATFORM_SYSTEM 0 -#endif #ifdef __cplusplus extern "C" { From c3f6a31b37f340f4d67f5b9c6eb20d2e39f43b02 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Wed, 4 Feb 2015 19:02:39 +0100 Subject: [PATCH 763/788] mali: remove 'mali_gpu_clk' from platform source This GPU clock setting is never actually used anywhere. The clock setup is currently done entirely in the DT. --- drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c b/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c index 224cf7db558789..6b61b991c8ca7c 100644 --- a/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c +++ b/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c @@ -41,10 +41,9 @@ mali_runtime_resume_table mali_runtime_resume = {400, 1100000}; static struct clk *sclk_g3d_clock = NULL; -/* Please take special care lowering these values, specially the voltage - * as it can cause system stability problems: random oops, usb hub resets */ -int mali_gpu_clk = 533; /* 533 MHz */ -int mali_gpu_vol = 1125000; /* 1.1125 V */ +/* Please take special care when lowering the voltage value, since it can * + * cause system stability problems (random oops, etc.) */ +unsigned int mali_gpu_vol = 1125000; /* 1.1125 V */ #ifdef CONFIG_MALI_DVFS #define MALI_DVFS_DEFAULT_STEP 0 From ca5019d97e625cf5906dd4318d295018681d25d2 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Wed, 4 Feb 2015 19:06:21 +0100 Subject: [PATCH 764/788] mali: remove used variable 'gpu_power_state' --- drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c b/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c index 6b61b991c8ca7c..b28788c350be26 100644 --- a/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c +++ b/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c @@ -49,7 +49,6 @@ unsigned int mali_gpu_vol = 1125000; /* 1.1125 V */ #define MALI_DVFS_DEFAULT_STEP 0 #endif -int gpu_power_state; static int bPoweroff; #ifdef CONFIG_REGULATOR @@ -170,8 +169,6 @@ static mali_bool init_mali_clock(void) static mali_bool initialized = MALI_FALSE; mali_bool ret = MALI_TRUE; - gpu_power_state = 0; - if (initialized) return ret; // already initialized From 2050a58c30b17c59bf405bd00b95463d22f0944d Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Wed, 4 Feb 2015 20:09:27 +0100 Subject: [PATCH 765/788] mali: platform: clean-up phase 1 --- .../platform/pegasus-m400/mali_platform.c | 42 ++++++++----------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c b/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c index b28788c350be26..28a0a2ed2eecbe 100644 --- a/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c +++ b/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c @@ -83,7 +83,7 @@ unsigned int mali_regulator_get_usecount(void) struct regulator_dev *rdev; if ( IS_ERR_OR_NULL(g3d_regulator) ) { - MALI_DEBUG_PRINT(1, ("Mali platform: getting regulator use count failed\n") ); + MALI_PRINT_ERROR(("Mali platform: getting regulator use count failed\n")); return 0; } rdev = g3d_regulator->rdev; @@ -98,7 +98,7 @@ void mali_regulator_disable(void) return; } regulator_disable(g3d_regulator); - MALI_DEBUG_PRINT(1, ("regulator_disable -> use cnt: %d \n",mali_regulator_get_usecount())); + MALI_DEBUG_PRINT(3, ("regulator_disable -> use cnt: %d \n",mali_regulator_get_usecount())); } void mali_regulator_enable(void) @@ -110,7 +110,7 @@ void mali_regulator_enable(void) return; } regulator_enable(g3d_regulator); - MALI_DEBUG_PRINT(1, ("regulator_enable -> use cnt: %d \n",mali_regulator_get_usecount())); + MALI_DEBUG_PRINT(3, ("regulator_enable -> use cnt: %d \n",mali_regulator_get_usecount())); } void mali_regulator_set_voltage(int min_uV, int max_uV) @@ -137,7 +137,7 @@ void mali_regulator_set_voltage(int min_uV, int max_uV) } #endif -static mali_bool mali_clk_enable(void) +static int mali_platform_clk_enable(void) { struct device *dev = &mali_platform_device->dev; unsigned long rate; @@ -145,14 +145,14 @@ static mali_bool mali_clk_enable(void) sclk_g3d_clock = clk_get(dev, "sclk_g3d"); if (IS_ERR(sclk_g3d_clock)) { MALI_PRINT_ERROR(("Mali platform: failed to get source g3d clock\n")); - return MALI_FALSE; + return 1; } _mali_osk_mutex_wait(mali_dvfs_lock); if (clk_prepare_enable(sclk_g3d_clock) < 0) { MALI_PRINT_ERROR(("Mali platform: failed to enable source g3d clock\n")); - return MALI_FALSE; + return 1; } rate = clk_get_rate(sclk_g3d_clock); @@ -161,25 +161,19 @@ static mali_bool mali_clk_enable(void) _mali_osk_mutex_signal(mali_dvfs_lock); - return MALI_TRUE; + return 0; } -static mali_bool init_mali_clock(void) +static int mali_platform_init_clk(void) { - static mali_bool initialized = MALI_FALSE; - mali_bool ret = MALI_TRUE; + static int initialized = 0; - if (initialized) - return ret; // already initialized + if (initialized) return 1; mali_dvfs_lock = _mali_osk_mutex_init(0, 0); - if (mali_dvfs_lock == NULL) - return _MALI_OSK_ERR_FAULT; + if (mali_dvfs_lock == NULL) return 1; - if (mali_clk_enable() == MALI_FALSE) - return MALI_FALSE; - - MALI_PRINT(("init_mali_clock\n")); + if (mali_platform_clk_enable()) return 1; #ifdef CONFIG_REGULATOR #ifdef USING_MALI_PMM @@ -189,8 +183,7 @@ static mali_bool init_mali_clock(void) #endif if (IS_ERR(g3d_regulator)) { - MALI_DEBUG_PRINT(1, ("Mali platform: failed to get g3d regulator\n") ); - ret = MALI_FALSE; + MALI_PRINT_ERROR(("Mali platform: failed to get g3d regulator\n")); goto err_regulator; } @@ -199,20 +192,19 @@ static mali_bool init_mali_clock(void) mali_regulator_set_voltage(mali_gpu_vol, mali_gpu_vol); #endif - MALI_DEBUG_PRINT(2, ("MALI Clock is set at mali driver\n")); - initialized = MALI_TRUE; - return MALI_TRUE; + initialized = 1; + return 0; #ifdef CONFIG_REGULATOR err_regulator: regulator_put(g3d_regulator); #endif - return ret; + return 1; } _mali_osk_errcode_t mali_platform_init() { - MALI_CHECK(init_mali_clock(), _MALI_OSK_ERR_FAULT); + MALI_CHECK(mali_platform_init_clk() == 0, _MALI_OSK_ERR_FAULT); #ifdef CONFIG_MALI_DVFS if (!clk_register_map) clk_register_map = _mali_osk_mem_mapioregion(CLK_DIV_STAT_G3D, 0x20, CLK_DESC); From 023822e622bcd99221dc17f7ce69de3eaae39535 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Wed, 4 Feb 2015 20:21:32 +0100 Subject: [PATCH 766/788] mali: platform: clean-up phase 2 --- drivers/gpu/arm/mali/platform/mali_platform.h | 11 -------- .../platform/pegasus-m400/mali_platform.c | 27 ++----------------- 2 files changed, 2 insertions(+), 36 deletions(-) diff --git a/drivers/gpu/arm/mali/platform/mali_platform.h b/drivers/gpu/arm/mali/platform/mali_platform.h index 5889fc97f65436..b33dd21af4bee9 100644 --- a/drivers/gpu/arm/mali/platform/mali_platform.h +++ b/drivers/gpu/arm/mali/platform/mali_platform.h @@ -88,17 +88,6 @@ void mali_gpu_utilization_handler(u32 utilization); */ void mali_utilization_suspend(void); -#ifdef CONFIG_REGULATOR -unsigned int mali_regulator_get_usecount(void); -void mali_regulator_disable(void); -void mali_regulator_enable(void); -void mali_regulator_set_voltage(int min_uV, int max_uV); -#endif - -mali_bool mali_clk_set_rate(unsigned int clk, unsigned int mhz); -unsigned long mali_clk_get_rate(void); -void mali_clk_put(mali_bool binc_mali_clk); - #ifdef MALI_PMM_RUNTIME_JOB_CONTROL_ON _mali_osk_errcode_t mali_platform_powerdown(u32 cores); _mali_osk_errcode_t mali_platform_powerup(u32 cores); diff --git a/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c b/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c index 28a0a2ed2eecbe..b39db4198f0dc3 100644 --- a/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c +++ b/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c @@ -78,7 +78,7 @@ void mali_set_runtime_resume_params(int clk, int volt) } #ifdef CONFIG_REGULATOR -unsigned int mali_regulator_get_usecount(void) +static unsigned int mali_regulator_get_usecount(void) { struct regulator_dev *rdev; @@ -90,30 +90,7 @@ unsigned int mali_regulator_get_usecount(void) return rdev->use_count; } -void mali_regulator_disable(void) -{ - bPoweroff = 1; - if ( IS_ERR_OR_NULL(g3d_regulator) ) { - MALI_DEBUG_PRINT(1, ("error on mali_regulator_disable : g3d_regulator is null\n")); - return; - } - regulator_disable(g3d_regulator); - MALI_DEBUG_PRINT(3, ("regulator_disable -> use cnt: %d \n",mali_regulator_get_usecount())); -} - -void mali_regulator_enable(void) -{ - bPoweroff = 0; - if( IS_ERR_OR_NULL(g3d_regulator) ) - { - MALI_DEBUG_PRINT(1, ("error on mali_regulator_enable : g3d_regulator is null\n")); - return; - } - regulator_enable(g3d_regulator); - MALI_DEBUG_PRINT(3, ("regulator_enable -> use cnt: %d \n",mali_regulator_get_usecount())); -} - -void mali_regulator_set_voltage(int min_uV, int max_uV) +static void mali_regulator_set_voltage(int min_uV, int max_uV) { int voltage; #ifndef CONFIG_MALI_DVFS From 2283051512aeb8b6fa79563f4d74fb8000a966c7 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Wed, 4 Feb 2015 20:30:47 +0100 Subject: [PATCH 767/788] mali: platform: clean-up phase 3 --- drivers/gpu/arm/mali/platform/mali_platform.h | 23 ----------- .../platform/pegasus-m400/mali_platform.c | 40 ------------------- 2 files changed, 63 deletions(-) diff --git a/drivers/gpu/arm/mali/platform/mali_platform.h b/drivers/gpu/arm/mali/platform/mali_platform.h index b33dd21af4bee9..5b8a0d4d05bcda 100644 --- a/drivers/gpu/arm/mali/platform/mali_platform.h +++ b/drivers/gpu/arm/mali/platform/mali_platform.h @@ -70,16 +70,6 @@ _mali_osk_errcode_t mali_platform_deinit(void); */ _mali_osk_errcode_t mali_platform_power_mode_change(mali_power_mode power_mode); - -/** @brief Platform specific handling of GPU utilization data - * - * When GPU utilization data is enabled, this function will be - * periodically called. - * - * @param utilization The workload utilization of the Mali GPU. 0 = no utilization, 256 = full utilization. - */ -void mali_gpu_utilization_handler(u32 utilization); - /** @brief Setting the power domain of MALI * * This function sets the power domain of MALI if Linux run time power management is enabled @@ -93,19 +83,6 @@ _mali_osk_errcode_t mali_platform_powerdown(u32 cores); _mali_osk_errcode_t mali_platform_powerup(u32 cores); #endif -#ifdef CONFIG_MALI_DVFS -#define MALI_DVFS_STEPS 5 -mali_bool init_mali_dvfs_status(int step); -void deinit_mali_dvfs_status(void); -mali_bool mali_dvfs_handler(u32 utilization); -int mali_dvfs_is_running(void); -void mali_dvfs_late_resume(void); -int get_mali_dvfs_control_status(void); -mali_bool set_mali_dvfs_current_step(unsigned int step); -void mali_default_step_set(int step, mali_bool boostup); -int change_dvfs_tableset(int change_clk, int change_step); -#endif - void mali_set_runtime_resume_params(int clk, int volt); #ifdef __cplusplus diff --git a/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c b/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c index b39db4198f0dc3..0fc2db782f0d22 100644 --- a/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c +++ b/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c @@ -17,10 +17,6 @@ #include "mali_osk.h" #include "mali_platform.h" -#ifdef USING_MALI_PMM -#include "mali_pmm.h" -#endif - #include #include #include @@ -29,9 +25,6 @@ #include -#define CLK_DIV_STAT_G3D 0x1003C62C -#define CLK_DESC "clk-divider-status" - typedef struct mali_runtime_resumeTag { int clk; int vol; @@ -45,10 +38,6 @@ static struct clk *sclk_g3d_clock = NULL; * cause system stability problems (random oops, etc.) */ unsigned int mali_gpu_vol = 1125000; /* 1.1125 V */ -#ifdef CONFIG_MALI_DVFS -#define MALI_DVFS_DEFAULT_STEP 0 -#endif - static int bPoweroff; #ifdef CONFIG_REGULATOR @@ -93,10 +82,6 @@ static unsigned int mali_regulator_get_usecount(void) static void mali_regulator_set_voltage(int min_uV, int max_uV) { int voltage; -#ifndef CONFIG_MALI_DVFS - min_uV = mali_gpu_vol; - max_uV = mali_gpu_vol; -#endif _mali_osk_mutex_wait(mali_dvfs_lock); @@ -182,13 +167,6 @@ static int mali_platform_init_clk(void) _mali_osk_errcode_t mali_platform_init() { MALI_CHECK(mali_platform_init_clk() == 0, _MALI_OSK_ERR_FAULT); -#ifdef CONFIG_MALI_DVFS - if (!clk_register_map) - clk_register_map = _mali_osk_mem_mapioregion(CLK_DIV_STAT_G3D, 0x20, CLK_DESC); - - if (!init_mali_dvfs_status(MALI_DVFS_DEFAULT_STEP)) - MALI_DEBUG_PRINT(1, ("mali_platform_init failed\n")); -#endif MALI_SUCCESS; } @@ -208,23 +186,5 @@ _mali_osk_errcode_t mali_platform_deinit() } #endif -#ifdef CONFIG_MALI_DVFS - deinit_mali_dvfs_status(); - if (clk_register_map) { - _mali_osk_mem_unmapioregion(CLK_DIV_STAT_G3D, 0x20, clk_register_map); - clk_register_map = 0; - } -#endif - MALI_SUCCESS; } - -void mali_gpu_utilization_handler(u32 utilization) -{ - if (bPoweroff == 0) { -#ifdef CONFIG_MALI_DVFS - if (!mali_dvfs_handler(utilization)) - MALI_DEBUG_PRINT(1,( "error on mali dvfs status in utilization\n")); -#endif - } -} From 4cfa5b95bba2905c1f2ae2b7e9e7906ebb72fa92 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Wed, 4 Feb 2015 20:42:36 +0100 Subject: [PATCH 768/788] mali: platform: clean-up phase 4 --- .../platform/pegasus-m400/mali_platform.c | 49 ++++++------------- 1 file changed, 15 insertions(+), 34 deletions(-) diff --git a/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c b/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c index 0fc2db782f0d22..2f007e7c77247c 100644 --- a/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c +++ b/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c @@ -38,21 +38,7 @@ static struct clk *sclk_g3d_clock = NULL; * cause system stability problems (random oops, etc.) */ unsigned int mali_gpu_vol = 1125000; /* 1.1125 V */ -static int bPoweroff; - #ifdef CONFIG_REGULATOR -struct regulator { - struct device *dev; - struct list_head list; - unsigned int always_on:1; - int uA_load; - int min_uV; - int max_uV; - char *supply_name; - struct device_attribute dev_attr; - struct regulator_dev *rdev; - struct dentry *debugfs; -}; struct regulator *g3d_regulator = NULL; #endif @@ -67,35 +53,27 @@ void mali_set_runtime_resume_params(int clk, int volt) } #ifdef CONFIG_REGULATOR -static unsigned int mali_regulator_get_usecount(void) -{ - struct regulator_dev *rdev; - - if ( IS_ERR_OR_NULL(g3d_regulator) ) { - MALI_PRINT_ERROR(("Mali platform: getting regulator use count failed\n")); - return 0; - } - rdev = g3d_regulator->rdev; - return rdev->use_count; -} - -static void mali_regulator_set_voltage(int min_uV, int max_uV) +static int mali_regulator_set_voltage(int min_uV, int max_uV) { int voltage; _mali_osk_mutex_wait(mali_dvfs_lock); - if( IS_ERR_OR_NULL(g3d_regulator) ) { - MALI_DEBUG_PRINT(1, ("error on mali_regulator_set_voltage : g3d_regulator is null\n")); - return; + if (IS_ERR_OR_NULL(g3d_regulator)) { + MALI_PRINT_ERROR(("Mali platform: invalid g3d regulator\n")); + return 1; } - MALI_DEBUG_PRINT(2, ("= regulator_set_voltage: %d, %d \n", min_uV, max_uV)); + + MALI_DEBUG_PRINT(3, ("Mali platform: setting g3d regulator to: %d / %d uV\n", min_uV, max_uV)); regulator_set_voltage(g3d_regulator, min_uV, max_uV); + voltage = regulator_get_voltage(g3d_regulator); mali_gpu_vol = voltage; - MALI_DEBUG_PRINT(1, ("= regulator_get_voltage: %d \n", mali_gpu_vol)); + MALI_DEBUG_PRINT(3, ("Mali platform: g3d regulator set to: %d uV\n", mali_gpu_vol)); _mali_osk_mutex_signal(mali_dvfs_lock); + + return 0; } #endif @@ -150,8 +128,11 @@ static int mali_platform_init_clk(void) } regulator_enable(g3d_regulator); - MALI_DEBUG_PRINT(3, ("Mali platform: g3d regulator enabled (use count = %u)\n", mali_regulator_get_usecount())); - mali_regulator_set_voltage(mali_gpu_vol, mali_gpu_vol); + MALI_DEBUG_PRINT(3, ("Mali platform: g3d regulator enabled\n")); + + if (mali_regulator_set_voltage(mali_gpu_vol, mali_gpu_vol)) { + goto err_regulator; + } #endif initialized = 1; From ddab4d8b0eaa9fc045e92c5ee860396c7517afa4 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Wed, 4 Feb 2015 20:50:58 +0100 Subject: [PATCH 769/788] mali: platform: clean-up phase 5 --- drivers/gpu/arm/mali/platform/mali_platform.h | 2 -- .../platform/pegasus-m400/mali_platform.c | 33 +++++-------------- 2 files changed, 9 insertions(+), 26 deletions(-) diff --git a/drivers/gpu/arm/mali/platform/mali_platform.h b/drivers/gpu/arm/mali/platform/mali_platform.h index 5b8a0d4d05bcda..b63e2f1eba1ba6 100644 --- a/drivers/gpu/arm/mali/platform/mali_platform.h +++ b/drivers/gpu/arm/mali/platform/mali_platform.h @@ -83,8 +83,6 @@ _mali_osk_errcode_t mali_platform_powerdown(u32 cores); _mali_osk_errcode_t mali_platform_powerup(u32 cores); #endif -void mali_set_runtime_resume_params(int clk, int volt); - #ifdef __cplusplus } #endif diff --git a/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c b/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c index 2f007e7c77247c..7a42ad77cbd6e1 100644 --- a/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c +++ b/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c @@ -25,32 +25,15 @@ #include -typedef struct mali_runtime_resumeTag { - int clk; - int vol; -} mali_runtime_resume_table; - -mali_runtime_resume_table mali_runtime_resume = {400, 1100000}; - -static struct clk *sclk_g3d_clock = NULL; /* Please take special care when lowering the voltage value, since it can * * cause system stability problems (random oops, etc.) */ unsigned int mali_gpu_vol = 1125000; /* 1.1125 V */ -#ifdef CONFIG_REGULATOR -struct regulator *g3d_regulator = NULL; -#endif - - -mali_io_address clk_register_map = 0; -_mali_osk_mutex_t *mali_dvfs_lock = 0; +static struct regulator *g3d_regulator = NULL; +static struct clk *sclk_g3d_clock = NULL; +static _mali_osk_mutex_t *mali_dvfs_lock = NULL; -void mali_set_runtime_resume_params(int clk, int volt) -{ - mali_runtime_resume.clk = clk; - mali_runtime_resume.vol = volt; -} #ifdef CONFIG_REGULATOR static int mali_regulator_set_voltage(int min_uV, int max_uV) @@ -127,7 +110,10 @@ static int mali_platform_init_clk(void) goto err_regulator; } - regulator_enable(g3d_regulator); + if (regulator_enable(g3d_regulator)) { + MALI_PRINT_ERROR(("Mali platform: failed to enable g3d regulator\n")); + goto err_regulator; + } MALI_DEBUG_PRINT(3, ("Mali platform: g3d regulator enabled\n")); if (mali_regulator_set_voltage(mali_gpu_vol, mali_gpu_vol)) { @@ -145,14 +131,13 @@ static int mali_platform_init_clk(void) return 1; } -_mali_osk_errcode_t mali_platform_init() +_mali_osk_errcode_t mali_platform_init(void) { MALI_CHECK(mali_platform_init_clk() == 0, _MALI_OSK_ERR_FAULT); - MALI_SUCCESS; } -_mali_osk_errcode_t mali_platform_deinit() +_mali_osk_errcode_t mali_platform_deinit(void) { if (sclk_g3d_clock) { clk_disable_unprepare(sclk_g3d_clock); From 38984b58c564bfad716b3e904feacfa32a870739 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Wed, 4 Feb 2015 20:57:54 +0100 Subject: [PATCH 770/788] mali: platform: add exynos code from Tizen tree Unmodified import of drivers/gpu/arm/mali400/r4p0_rel0/platform/exynos/exynos.{c,h} from Tizen's linux-3.10.git ('tizen' branch) tree. --- drivers/gpu/arm/mali/platform/exynos/exynos.c | 376 ++++++++++++++++++ drivers/gpu/arm/mali/platform/exynos/exynos.h | 69 ++++ 2 files changed, 445 insertions(+) create mode 100644 drivers/gpu/arm/mali/platform/exynos/exynos.c create mode 100644 drivers/gpu/arm/mali/platform/exynos/exynos.h diff --git a/drivers/gpu/arm/mali/platform/exynos/exynos.c b/drivers/gpu/arm/mali/platform/exynos/exynos.c new file mode 100644 index 00000000000000..859ee362d1c29b --- /dev/null +++ b/drivers/gpu/arm/mali/platform/exynos/exynos.c @@ -0,0 +1,376 @@ +/* + * Mali400 platform glue for Samsung Exynos SoCs + * + * Copyright 2013 by Samsung Electronics Co., Ltd. + * Author: Tomasz Figa + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mali_kernel_common.h" +#include "mali_osk.h" + +#ifdef CONFIG_MALI400_PROFILING +#include "mali_osk_profiling.h" +#endif + +#include "exynos.h" + +struct mali_exynos_variant { + const struct mali_exynos_dvfs_step *steps; + unsigned int nr_steps; + unsigned int has_smmuclk; +}; + +struct mali_exynos_dvfs_step { + unsigned int rate; + unsigned int voltage; + unsigned int downthreshold; + unsigned int upthreshold; +}; + +struct mali_exynos_drvdata { + struct device *dev; + + const struct mali_exynos_dvfs_step *steps; + unsigned int nr_steps; + unsigned int has_smmuclk; + + struct clk *pll; + struct clk *mux1; + struct clk *mux2; + struct clk *sclk; + struct clk *smmu; + struct clk *g3d; + + struct regulator *vdd_g3d; + + mali_power_mode power_mode; + unsigned int dvfs_step; + unsigned int load; + + struct workqueue_struct *dvfs_workqueue; + struct work_struct dvfs_work; +}; + +extern struct platform_device *mali_platform_device; + +static struct mali_exynos_drvdata *mali; + +/* + * DVFS tables + */ + +#define MALI_DVFS_STEP(freq, voltage, down, up) \ + {freq, voltage, (256 * down) / 100, (256 * up) / 100} + +static const struct mali_exynos_dvfs_step mali_exynos_dvfs_step_3250[] = { + MALI_DVFS_STEP(134, 0, 0, 100) +}; + +static const struct mali_exynos_dvfs_step mali_exynos_dvfs_step_4210[] = { + MALI_DVFS_STEP(160, 950000, 0, 90), + MALI_DVFS_STEP(266, 1050000, 85, 100) +}; + +static const struct mali_exynos_dvfs_step mali_exynos_dvfs_step_4x12[] = { + MALI_DVFS_STEP(160, 875000, 0, 70), + MALI_DVFS_STEP(266, 900000, 62, 90), + MALI_DVFS_STEP(350, 950000, 85, 90), + MALI_DVFS_STEP(440, 1025000, 85, 100) +}; + +static const struct mali_exynos_dvfs_step mali_exynos_dvfs_step_4x12_prime[] = { + MALI_DVFS_STEP(160, 875000, 0, 70), + MALI_DVFS_STEP(266, 900000, 62, 90), + MALI_DVFS_STEP(350, 950000, 85, 90), + MALI_DVFS_STEP(440, 1025000, 85, 90), + MALI_DVFS_STEP(533, 1075000, 95, 100) +}; + +/* + * Variants + */ + +static const struct mali_exynos_variant mali_variant_3250 = { + .steps = mali_exynos_dvfs_step_3250, + .nr_steps = ARRAY_SIZE(mali_exynos_dvfs_step_3250), + .has_smmuclk = true, +}; + +static const struct mali_exynos_variant mali_variant_4210 = { + .steps = mali_exynos_dvfs_step_4210, + .nr_steps = ARRAY_SIZE(mali_exynos_dvfs_step_4210), +}; + +static const struct mali_exynos_variant mali_variant_4x12 = { + .steps = mali_exynos_dvfs_step_4x12, + .nr_steps = ARRAY_SIZE(mali_exynos_dvfs_step_4x12), +}; + +static const struct mali_exynos_variant mali_variant_4x12_prime = { + .steps = mali_exynos_dvfs_step_4x12_prime, + .nr_steps = ARRAY_SIZE(mali_exynos_dvfs_step_4x12_prime), +}; + +const struct of_device_id mali_of_matches[] = { + { .compatible = "samsung,exynos3250-g3d", + .data = &mali_variant_3250, }, + { .compatible = "samsung,exynos4210-g3d", + .data = &mali_variant_4210, }, + { .compatible = "samsung,exynos4x12-g3d", + .data = &mali_variant_4x12, }, + { .compatible = "samsung,exynos4x12-prime-g3d", + .data = &mali_variant_4x12_prime, }, + { /* Sentinel */ } +}; + +#ifdef CONFIG_MALI400_PROFILING +static inline void _mali_osk_profiling_add_gpufreq_event(int rate, int vol) +{ + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_SINGLE | + MALI_PROFILING_EVENT_CHANNEL_GPU | + MALI_PROFILING_EVENT_REASON_SINGLE_GPU_FREQ_VOLT_CHANGE, + rate, vol, 0, 0, 0); +} +#else +static inline void _mali_osk_profiling_add_gpufreq_event(int rate, int vol) +{ +} +#endif + +/* + * DVFS control + */ + +static void mali_exynos_set_dvfs_step(struct mali_exynos_drvdata *mali, + unsigned int step) +{ + const struct mali_exynos_dvfs_step *next = &mali->steps[step]; + + if (step <= mali->dvfs_step) + clk_set_rate(mali->sclk, next->rate * 1000000); + + regulator_set_voltage(mali->vdd_g3d, + next->voltage, next->voltage); + + if (step > mali->dvfs_step) + clk_set_rate(mali->sclk, next->rate * 1000000); + + _mali_osk_profiling_add_gpufreq_event(next->rate * 1000000, + regulator_get_voltage(mali->vdd_g3d) / 1000); + mali->dvfs_step = step; +} + +static void exynos_dvfs_work(struct work_struct *work) +{ + struct mali_exynos_drvdata *mali = container_of(work, + struct mali_exynos_drvdata, dvfs_work); + unsigned int step = mali->dvfs_step; + const struct mali_exynos_dvfs_step *cur = &mali->steps[step]; + + if (mali->load > cur->upthreshold) + ++step; + else if (mali->load < cur->downthreshold) + --step; + + BUG_ON(step >= mali->nr_steps); + + if (step != mali->dvfs_step) + mali_exynos_set_dvfs_step(mali, step); +} + +static void exynos_update_dvfs(struct mali_gpu_utilization_data *data) +{ + if (data->utilization_gpu > 255) + data->utilization_gpu = 255; + + mali->load = data->utilization_gpu; + + queue_work(mali->dvfs_workqueue, &mali->dvfs_work); +} + +/* + * Power management + */ + +_mali_osk_errcode_t mali_platform_power_mode_change(mali_power_mode power_mode) +{ + if (WARN_ON(mali->power_mode == power_mode)) + MALI_SUCCESS; + + switch (power_mode) { + case MALI_POWER_MODE_ON: + mali_exynos_set_dvfs_step(mali, 0); + clk_prepare_enable(mali->g3d); + clk_prepare_enable(mali->sclk); + if (mali->has_smmuclk) + clk_prepare_enable(mali->smmu); + break; + + case MALI_POWER_MODE_LIGHT_SLEEP: + case MALI_POWER_MODE_DEEP_SLEEP: + if (mali->has_smmuclk) + clk_disable_unprepare(mali->smmu); + clk_disable_unprepare(mali->sclk); + clk_disable_unprepare(mali->g3d); + _mali_osk_profiling_add_gpufreq_event(0, 0); + break; + } + + mali->power_mode = power_mode; + + MALI_SUCCESS; +} + +/* + * Platform-specific initialization/cleanup + */ + +static struct mali_gpu_device_data mali_exynos_gpu_data = { + .shared_mem_size = SZ_256M, + .fb_start = 0x40000000, + .fb_size = 0xb1000000, + .utilization_interval = 100, /* 100ms in Tizen */ + .utilization_callback = exynos_update_dvfs, +}; + +_mali_osk_errcode_t mali_platform_init(void) +{ + struct platform_device *pdev = mali_platform_device; + const struct mali_exynos_variant *variant; + const struct of_device_id *match; + struct resource *old_res, *new_res; + unsigned int i, irq_res, mem_res; + struct device_node *np; + int ret; + + if (WARN_ON(!pdev)) + return -ENODEV; + + MALI_DEBUG_PRINT(4, ("mali_platform_device_register() called\n")); + + pdev->dev.platform_data = &mali_exynos_gpu_data; + + np = pdev->dev.of_node; + if (WARN_ON(!np)) + return -ENODEV; + + match = of_match_node(mali_of_matches, np); + if (WARN_ON(!match)) + return -ENODEV; + + variant = match->data; + + old_res = pdev->resource; + new_res = kzalloc(sizeof(*new_res) * pdev->num_resources, GFP_KERNEL); + if (WARN_ON(!new_res)) + return -ENOMEM; + + /* Copy first resource */ + memcpy(new_res, old_res++, sizeof(*new_res)); + + /* Rearrange next resources */ + irq_res = 0; + mem_res = 0; + for (i = 1; i < pdev->num_resources; ++i, ++old_res) { + if (resource_type(old_res) == IORESOURCE_MEM) + memcpy(&new_res[1 + 2 * mem_res++], + old_res, sizeof(*old_res)); + else if (resource_type(old_res) == IORESOURCE_IRQ) + memcpy(&new_res[2 + 2 * irq_res++], + old_res, sizeof(*old_res)); + } + + kfree(pdev->resource); + pdev->resource = new_res; + + mali = devm_kzalloc(&pdev->dev, sizeof(*mali), GFP_KERNEL); + if (WARN_ON(!mali)) + return -ENOMEM; + + mali->dev = &pdev->dev; + mali->steps = variant->steps; + mali->nr_steps = variant->nr_steps; + mali->has_smmuclk = variant->has_smmuclk; + + mali->pll = devm_clk_get(mali->dev, "pll"); + if (WARN_ON(IS_ERR(mali->pll))) + return PTR_ERR(mali->pll); + + mali->mux1 = devm_clk_get(mali->dev, "mux1"); + if (WARN_ON(IS_ERR(mali->mux1))) + return PTR_ERR(mali->mux1); + + mali->mux2 = devm_clk_get(mali->dev, "mux2"); + if (WARN_ON(IS_ERR(mali->mux2))) + return PTR_ERR(mali->mux2); + + mali->sclk = devm_clk_get(mali->dev, "sclk"); + if (WARN_ON(IS_ERR(mali->sclk))) + return PTR_ERR(mali->sclk); + + if (mali->has_smmuclk) { + mali->smmu = devm_clk_get(mali->dev, "smmu"); + if (WARN_ON(IS_ERR(mali->smmu))) + return PTR_ERR(mali->smmu); + } + + mali->g3d = devm_clk_get(mali->dev, "g3d"); + if (WARN_ON(IS_ERR(mali->g3d))) + return PTR_ERR(mali->g3d); + + mali->vdd_g3d = devm_regulator_get(mali->dev, "vdd_g3d"); + if (WARN_ON(IS_ERR(mali->vdd_g3d))) + return PTR_ERR(mali->vdd_g3d); + + mali->dvfs_workqueue = create_singlethread_workqueue("mali_dvfs"); + if (WARN_ON(!mali->dvfs_workqueue)) + return -EFAULT; + + mali->power_mode = MALI_POWER_MODE_LIGHT_SLEEP; + + INIT_WORK(&mali->dvfs_work, exynos_dvfs_work); + + ret = regulator_enable(mali->vdd_g3d); + if (WARN_ON(ret)) { + destroy_workqueue(mali->dvfs_workqueue); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + clk_set_parent(mali->mux1, mali->pll); + clk_set_parent(mali->mux2, mali->mux1); + mali_exynos_set_dvfs_step(mali, 0); + + pm_runtime_set_autosuspend_delay(&pdev->dev, 300); + pm_runtime_use_autosuspend(&pdev->dev); + + pm_runtime_enable(&pdev->dev); + + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_platform_deinit(void) +{ + struct platform_device *pdev = mali_platform_device; + + pm_runtime_disable(&pdev->dev); + + regulator_disable(mali->vdd_g3d); + + _mali_osk_profiling_add_gpufreq_event(0, 0); + + MALI_SUCCESS; +} diff --git a/drivers/gpu/arm/mali/platform/exynos/exynos.h b/drivers/gpu/arm/mali/platform/exynos/exynos.h new file mode 100644 index 00000000000000..dd03d6c1b166c5 --- /dev/null +++ b/drivers/gpu/arm/mali/platform/exynos/exynos.h @@ -0,0 +1,69 @@ +/* + * Mali400 platform glue for Samsung Exynos SoCs + * + * Copyright 2013 by Samsung Electronics Co., Ltd. + * Author: Tomasz Figa + * + * 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. + */ + +#ifndef __EXYNOS_H__ +#define __EXYNOS_H__ + +#include + +#include "mali_osk.h" + +/** @brief description of power change reasons + */ +typedef enum mali_power_mode_tag +{ + MALI_POWER_MODE_ON, + MALI_POWER_MODE_LIGHT_SLEEP, + MALI_POWER_MODE_DEEP_SLEEP, +} mali_power_mode; + +/** @brief Platform specific setup and initialisation of MALI + * + * This is called from the entrypoint of the driver to initialize the platform + * + * @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error. + */ +_mali_osk_errcode_t mali_platform_init(void); + +/** @brief Platform specific deinitialisation of MALI + * + * This is called on the exit of the driver to terminate the platform + * + * @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error. + */ +_mali_osk_errcode_t mali_platform_deinit(void); + +/** @brief Platform specific powerdown sequence of MALI + * + * Call as part of platform init if there is no PMM support, else the + * PMM will call it. + * There are three power modes defined: + * 1) MALI_POWER_MODE_ON + * 2) MALI_POWER_MODE_LIGHT_SLEEP + * 3) MALI_POWER_MODE_DEEP_SLEEP + * MALI power management module transitions to MALI_POWER_MODE_LIGHT_SLEEP mode when MALI is idle + * for idle timer (software timer defined in mali_pmm_policy_jobcontrol.h) duration, MALI transitions + * to MALI_POWER_MODE_LIGHT_SLEEP mode during timeout if there are no more jobs queued. + * MALI power management module transitions to MALI_POWER_MODE_DEEP_SLEEP mode when OS does system power + * off. + * Customer has to add power down code when MALI transitions to MALI_POWER_MODE_LIGHT_SLEEP or MALI_POWER_MODE_DEEP_SLEEP + * mode. + * MALI_POWER_MODE_ON mode is entered when the MALI is to powered up. Some customers want to control voltage regulators during + * the whole system powers on/off. Customer can track in this function whether the MALI is powered up from + * MALI_POWER_MODE_LIGHT_SLEEP or MALI_POWER_MODE_DEEP_SLEEP mode and manage the voltage regulators as well. + * @param power_mode defines the power modes + * @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error. + */ +_mali_osk_errcode_t mali_platform_power_mode_change(mali_power_mode power_mode); + +extern const struct of_device_id mali_of_matches[]; + +#endif From 4607a1790de07477f0512df29ba13ce52f5c4517 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Wed, 4 Feb 2015 22:00:20 +0100 Subject: [PATCH 771/788] mali: implement platform selection Based on code from Tizen kernel. Also move mali_platform_{init,deinit} into mali_{probe,remove}, like 'suggested' by the Tizen code. --- drivers/gpu/arm/mali/Kbuild | 7 ++++++- drivers/gpu/arm/mali/common/mali_kernel_core.c | 5 ----- drivers/gpu/arm/mali/linux/mali_kernel_linux.c | 9 +++++++++ .../mali/platform/exynos/{exynos.h => mali_platform.h} | 0 .../arm/mali/platform/{ => pegasus-m400}/mali_platform.h | 0 .../pegasus-m400/{mali_platform.c => pegasus-m400.c} | 0 6 files changed, 15 insertions(+), 6 deletions(-) rename drivers/gpu/arm/mali/platform/exynos/{exynos.h => mali_platform.h} (100%) rename drivers/gpu/arm/mali/platform/{ => pegasus-m400}/mali_platform.h (100%) rename drivers/gpu/arm/mali/platform/pegasus-m400/{mali_platform.c => pegasus-m400.c} (100%) diff --git a/drivers/gpu/arm/mali/Kbuild b/drivers/gpu/arm/mali/Kbuild index c000dbf22a6374..e6bf9b8417b262 100644 --- a/drivers/gpu/arm/mali/Kbuild +++ b/drivers/gpu/arm/mali/Kbuild @@ -22,6 +22,9 @@ MALI_PP_SCHEDULER_FORCE_NO_JOB_OVERLAP_BETWEEN_APPS ?= 0 MALI_UPPER_HALF_SCHEDULING ?= 1 MALI_ENABLE_CPU_CYCLES ?= 0 +MALI_PLATFORM = pegasus-m400 +MALI_PLATFORM_FILES = $(subst $(src)/,,$(wildcard $(src)/platform/$(MALI_PLATFORM)/*.c)) + # For customer releases the Linux Device Drivers will be provided as ARM proprietary and GPL releases: # The ARM proprietary product will only include the license/proprietary directory # The GPL product will only include the license/gpl directory @@ -103,7 +106,6 @@ mali-y += \ common/mali_pm_domain.o \ linux/mali_osk_pm.o \ linux/mali_pmu_power_up_down.o \ - platform/pegasus-m400/mali_platform.o \ __malidrv_build_info.o ifneq ($(MALI_PLATFORM_FILES),) @@ -147,6 +149,9 @@ ccflags-$(CONFIG_MALI400_DEBUG) += -DDEBUG # Use our defines when compiling ccflags-y += -I$(src) -I$(src)/include -I$(src)/common -I$(src)/linux -I$(src)/platform +# Platform integration +ccflags-y += -I$(src)/platform/$(MALI_PLATFORM) + # Get subversion revision number, fall back to only ${MALI_RELEASE_NAME} if no svn info is available MALI_RELEASE_NAME=$(shell cat $(src)/.version 2> /dev/null) diff --git a/drivers/gpu/arm/mali/common/mali_kernel_core.c b/drivers/gpu/arm/mali/common/mali_kernel_core.c index 9256feaeb473fd..36c47997e87b5d 100644 --- a/drivers/gpu/arm/mali/common/mali_kernel_core.c +++ b/drivers/gpu/arm/mali/common/mali_kernel_core.c @@ -929,8 +929,6 @@ _mali_osk_errcode_t mali_initialize_subsystems(void) if (_MALI_OSK_ERR_OK != err) goto pmu_reset_failed; } - mali_platform_init(); - /* Detect which Mali GPU we are dealing with */ err = mali_parse_product_info(); if (_MALI_OSK_ERR_OK != err) goto product_info_parsing_failed; @@ -1040,9 +1038,6 @@ void mali_terminate_subsystems(void) MALI_DEBUG_PRINT(2, ("terminate_subsystems() called\n")); - /* shut down subsystems in reverse order from startup */ - mali_platform_deinit(); - /* We need the GPU to be powered up for the terminate sequence */ _mali_osk_pm_dev_ref_add(); diff --git a/drivers/gpu/arm/mali/linux/mali_kernel_linux.c b/drivers/gpu/arm/mali/linux/mali_kernel_linux.c index 1a0684ea593793..730468eabaf9db 100644 --- a/drivers/gpu/arm/mali/linux/mali_kernel_linux.c +++ b/drivers/gpu/arm/mali/linux/mali_kernel_linux.c @@ -41,6 +41,8 @@ #include "mali_profiling_internal.h" #endif +#include + /* Streamline support for the Mali driver */ #if defined(CONFIG_TRACEPOINTS) && defined(CONFIG_MALI400_PROFILING) /* Ask Linux to create the tracepoints */ @@ -537,6 +539,12 @@ static int mali_probe(struct platform_device *pdev) mali_parse_dt(pdev); mali_platform_device = pdev; + if (mali_platform_init() != _MALI_OSK_ERR_OK) + { + MALI_PRINT_ERROR(("mali_probe(): mali_platform_init() failed.")); + return -ENODEV; + } + if (_MALI_OSK_ERR_OK == _mali_osk_wq_init()) { /* Initialize the Mali GPU HW specified by pdev */ if (_MALI_OSK_ERR_OK == mali_initialize_subsystems()) { @@ -573,6 +581,7 @@ static int mali_remove(struct platform_device *pdev) mali_miscdevice_unregister(); mali_terminate_subsystems(); _mali_osk_wq_term(); + mali_platform_deinit(); mali_platform_device = NULL; return 0; } diff --git a/drivers/gpu/arm/mali/platform/exynos/exynos.h b/drivers/gpu/arm/mali/platform/exynos/mali_platform.h similarity index 100% rename from drivers/gpu/arm/mali/platform/exynos/exynos.h rename to drivers/gpu/arm/mali/platform/exynos/mali_platform.h diff --git a/drivers/gpu/arm/mali/platform/mali_platform.h b/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.h similarity index 100% rename from drivers/gpu/arm/mali/platform/mali_platform.h rename to drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.h diff --git a/drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c b/drivers/gpu/arm/mali/platform/pegasus-m400/pegasus-m400.c similarity index 100% rename from drivers/gpu/arm/mali/platform/pegasus-m400/mali_platform.c rename to drivers/gpu/arm/mali/platform/pegasus-m400/pegasus-m400.c From 2a69b65218cb07172a3c7d9773705f6fd470ff54 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Wed, 4 Feb 2015 22:03:40 +0100 Subject: [PATCH 772/788] mali: disable runtime PM in mali_platform_deinit This should disable the G3D powerdomain on Mali deinitialization. Based on patch by Alban Browaeys . --- drivers/gpu/arm/mali/platform/pegasus-m400/pegasus-m400.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/arm/mali/platform/pegasus-m400/pegasus-m400.c b/drivers/gpu/arm/mali/platform/pegasus-m400/pegasus-m400.c index 7a42ad77cbd6e1..7e4179f5c2f2e4 100644 --- a/drivers/gpu/arm/mali/platform/pegasus-m400/pegasus-m400.c +++ b/drivers/gpu/arm/mali/platform/pegasus-m400/pegasus-m400.c @@ -22,6 +22,7 @@ #include #include #include +#include #include @@ -152,5 +153,9 @@ _mali_osk_errcode_t mali_platform_deinit(void) } #endif +#ifdef CONFIG_PM + pm_runtime_disable(&(mali_platform_device->dev)); +#endif + MALI_SUCCESS; } From 72cdb405af2c49a589b815e6f19566fd7edabb90 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sat, 7 Feb 2015 22:13:57 +0100 Subject: [PATCH 773/788] mali: platform: control both sclk_g3d and g3d Upstream commit b511593d7165809019a5b84b35adf95f284410a8 has changed how the clocks for the G3D block work, so controlling only sclk_g3d doesn't work anymore. --- .../boot/dts/exynos4412-odroid-common.dtsi | 4 +-- .../mali/platform/pegasus-m400/pegasus-m400.c | 27 ++++++++++++++++++- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index 15e574dcddd2b6..d73a19e15cbd02 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -675,8 +675,8 @@ assigned-clock-parents = <&clock CLK_SCLK_VPLL>, <&clock CLK_MOUT_G3D1>; assigned-clock-rates = <0>, <0>, <533000000>, <533000000>; - clocks = <&clock CLK_SCLK_G3D>; - clock-names = "sclk_g3d"; + clocks = <&clock CLK_SCLK_G3D>, <&clock CLK_G3D>; + clock-names = "sclk_g3d", "g3d"; reg = <0x13001000 0x200>, <0x13000000 0x100>, <0x13003000 0x100>, <0x13008000 0x1100>, <0x13004000 0x100>, <0x1300A000 0x1100>, diff --git a/drivers/gpu/arm/mali/platform/pegasus-m400/pegasus-m400.c b/drivers/gpu/arm/mali/platform/pegasus-m400/pegasus-m400.c index 7e4179f5c2f2e4..745aede5383f1c 100644 --- a/drivers/gpu/arm/mali/platform/pegasus-m400/pegasus-m400.c +++ b/drivers/gpu/arm/mali/platform/pegasus-m400/pegasus-m400.c @@ -31,11 +31,15 @@ * cause system stability problems (random oops, etc.) */ unsigned int mali_gpu_vol = 1125000; /* 1.1125 V */ -static struct regulator *g3d_regulator = NULL; static struct clk *sclk_g3d_clock = NULL; +static struct clk *g3d_clock = NULL; + +static struct regulator *g3d_regulator = NULL; static _mali_osk_mutex_t *mali_dvfs_lock = NULL; + + #ifdef CONFIG_REGULATOR static int mali_regulator_set_voltage(int min_uV, int max_uV) { @@ -72,6 +76,12 @@ static int mali_platform_clk_enable(void) return 1; } + g3d_clock = clk_get(dev, "g3d"); + if (IS_ERR(g3d_clock)) { + MALI_PRINT_ERROR(("Mali platform: failed to get g3d clock\n")); + return 1; + } + _mali_osk_mutex_wait(mali_dvfs_lock); if (clk_prepare_enable(sclk_g3d_clock) < 0) { @@ -79,8 +89,17 @@ static int mali_platform_clk_enable(void) return 1; } + if (clk_prepare_enable(g3d_clock) < 0) { + MALI_PRINT_ERROR(("Mali platform: failed to enable g3d clock\n")); + return 1; + } + rate = clk_get_rate(sclk_g3d_clock); + MALI_PRINT(("Mali platform: source g3d clock rate = %u MHz\n", rate / 1000000)); + + rate = clk_get_rate(g3d_clock); + MALI_PRINT(("Mali platform: g3d clock rate = %u MHz\n", rate / 1000000)); _mali_osk_mutex_signal(mali_dvfs_lock); @@ -140,6 +159,12 @@ _mali_osk_errcode_t mali_platform_init(void) _mali_osk_errcode_t mali_platform_deinit(void) { + if (g3d_clock) { + clk_disable_unprepare(g3d_clock); + clk_put(g3d_clock); + g3d_clock = NULL; + } + if (sclk_g3d_clock) { clk_disable_unprepare(sclk_g3d_clock); clk_put(sclk_g3d_clock); From 673bd138a17d2b869bd81caa59c667decfc35217 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Mon, 9 Feb 2015 21:39:12 +0100 Subject: [PATCH 774/788] mali: platform: import pegasus-m400 dvfs source Currently deactivated by preprocessor macro. --- .../platform/pegasus-m400/pegasus-m400-dvfs.c | 432 ++++++++++++++++++ 1 file changed, 432 insertions(+) create mode 100644 drivers/gpu/arm/mali/platform/pegasus-m400/pegasus-m400-dvfs.c diff --git a/drivers/gpu/arm/mali/platform/pegasus-m400/pegasus-m400-dvfs.c b/drivers/gpu/arm/mali/platform/pegasus-m400/pegasus-m400-dvfs.c new file mode 100644 index 00000000000000..059dae8a16ee48 --- /dev/null +++ b/drivers/gpu/arm/mali/platform/pegasus-m400/pegasus-m400-dvfs.c @@ -0,0 +1,432 @@ +/* + * Copyright (C) 2010 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_platform_dvfs.c + * Platform specific Mali driver dvfs functions + */ + +#if 0 + +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_platform.h" + +#include +#include +#include +#include + +#include + +#include "mali_device_pause_resume.h" +#include + +#define MAX_MALI_DVFS_STEPS 5 +#define MALI_DVFS_WATING 10 // msec + +#ifdef CONFIG_EXYNOS_ASV +#include +#include +#endif + +static int bMaliDvfsRun = 0; + +typedef struct mali_dvfs_tableTag { + unsigned int clock; + unsigned int freq; + unsigned int vol; +} mali_dvfs_table; + +typedef struct mali_dvfs_statusTag { + unsigned int currentStep; + mali_dvfs_table * pCurrentDvfs; + +} mali_dvfs_currentstatus; + +typedef struct mali_dvfs_thresholdTag { + unsigned int downthreshold; + unsigned int upthreshold; +}m ali_dvfs_threshold_table; + +typedef struct mali_dvfs_staycount { + unsigned int staycount; +} mali_dvfs_staycount_table; + +typedef struct mali_dvfs_stepTag { + int clk; + int vol; +} mali_dvfs_step; + +mali_dvfs_step step[MALI_DVFS_STEPS] = { + /*step 0 clk*/ {160, 875000}, +#if (MALI_DVFS_STEPS > 1) + /*step 1 clk*/ {266, 900000}, +#if (MALI_DVFS_STEPS > 2) + /*step 2 clk*/ {350, 950000}, +#if (MALI_DVFS_STEPS > 3) + /*step 3 clk*/ {440, 1025000}, +#if (MALI_DVFS_STEPS > 4) + /*step 4 clk*/ {533, 1075000} +#endif +#endif +#endif +#endif +}; + +mali_dvfs_staycount_table mali_dvfs_staycount[MALI_DVFS_STEPS] = { + /*step 0*/{1}, +#if (MALI_DVFS_STEPS > 1) + /*step 1*/{1}, +#if (MALI_DVFS_STEPS > 2) + /*step 2*/{1}, +#if (MALI_DVFS_STEPS > 3) + /*step 3*/{1}, +#if (MALI_DVFS_STEPS > 4) + /*step 4*/{0} +#endif +#endif +#endif +#endif +}; + +/* dvfs information + + L0 = 533Mhz, 1.075V + L1 = 440Mhz, 1.025V + L2 = 350Mhz, 0.95V + L3 = 266Mhz, 0.90V + L4 = 160Mhz, 0.875V +*/ + +mali_dvfs_table mali_dvfs_all[MAX_MALI_DVFS_STEPS] = { + {160 ,1000000 , 875000}, + {266 ,1000000 , 900000}, + {350 ,1000000 , 950000}, + {440 ,1000000 , 1025000}, + {533 ,1000000 , 1075000} }; + +mali_dvfs_table mali_dvfs[MALI_DVFS_STEPS] = { + {160 ,1000000 , 875000}, +#if (MALI_DVFS_STEPS > 1) + {266 ,1000000 , 901000}, +#if (MALI_DVFS_STEPS > 2) + {350 ,1000000 , 951000}, +#if (MALI_DVFS_STEPS > 3) + {440 ,1000000 ,1025100}, +#if (MALI_DVFS_STEPS > 4) + {533 ,1000000 ,1075000} +#endif +#endif +#endif +#endif +}; + +mali_dvfs_threshold_table mali_dvfs_threshold[MALI_DVFS_STEPS] = { + {0 , 50}, +#if (MALI_DVFS_STEPS > 1) + {45 , 60}, +#if (MALI_DVFS_STEPS > 2) + {50 , 70}, +#if (MALI_DVFS_STEPS > 3) + {75 , 85}, +#if (MALI_DVFS_STEPS > 4) + {80 , 100} +#endif +#endif +#endif +#endif +}; + +#ifdef CONFIG_EXYNOS_ASV +#define ASV_LEVEL 12 /* ASV0, 1, 11 is reserved */ +#define ASV_LEVEL_PRIME 13 /* ASV0, 1, 12 is reserved */ + +static unsigned int asv_3d_volt_9_table[MALI_DVFS_STEPS][ASV_LEVEL] = { + { 950000, 925000, 900000, 900000, 875000, 875000, 875000, 875000, 875000, 875000, 875000, 850000}, /* L3(160Mhz) */ +#if (MALI_DVFS_STEPS > 1) + { 975000, 950000, 925000, 925000, 925000, 900000, 900000, 875000, 875000, 875000, 875000, 850000}, /* L2(266Mhz) */ +#if (MALI_DVFS_STEPS > 2) + { 1050000, 1025000, 1000000, 1000000, 975000, 950000, 950000, 950000, 925000, 925000, 925000, 900000}, /* L1(350Mhz) */ +#if (MALI_DVFS_STEPS > 3) + { 1100000, 1075000, 1050000, 1050000, 1050000, 1025000, 1025000, 1000000, 1000000, 1000000, 1100000, 950000}, /* L0(440Mhz) */ +#endif +#endif +#endif +}; + +static unsigned int asv_3d_volt_9_table_for_prime[MALI_DVFS_STEPS][ASV_LEVEL_PRIME] = { + { 950000, 937500, 925000, 912500, 900000, 887500, 875000, 862500, 875000, 862500, 850000, 850000, 850000}, /* L4(160Mhz) */ +#if (MALI_DVFS_STEPS > 1) + { 975000, 962500, 950000, 937500, 925000, 912500, 900000, 887500, 900000, 887500, 875000, 875000, 875000}, /* L3(266Mhz) */ +#if (MALI_DVFS_STEPS > 2) + { 1025000, 1012500, 1000000, 987500, 975000, 962500, 950000, 937500, 950000, 937500, 912500, 900000, 887500}, /* L2(350Mhz) */ +#if (MALI_DVFS_STEPS > 3) + { 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000, 1012500, 1000000, 975000, 962500, 950000}, /* L1(440Mhz) */ +#if (MALI_DVFS_STEPS > 4) + { 1150000, 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, 1075000, 1062500, 1037500, 1025000, 1012500}, /* L0(533Mhz) */ +#endif +#endif +#endif +#endif +}; + +#endif + +/* dvfs status */ +mali_dvfs_currentstatus maliDvfsStatus; +int mali_dvfs_control = 0; + +static u32 mali_dvfs_utilization = 255; + +static void mali_dvfs_work_handler(struct work_struct *w); + +static struct workqueue_struct *mali_dvfs_wq = 0; +extern mali_io_address clk_register_map; +extern _mali_osk_lock_t *mali_dvfs_lock; + + +static DECLARE_WORK(mali_dvfs_work, mali_dvfs_work_handler); + +static inline unsigned int get_mali_dvfs_status(void) +{ + return maliDvfsStatus.currentStep; +} + +mali_bool set_mali_dvfs_current_step(unsigned int step) +{ + _mali_osk_lock_wait(mali_dvfs_lock, _MALI_OSK_LOCKMODE_RW); + maliDvfsStatus.currentStep = step; + _mali_osk_lock_signal(mali_dvfs_lock, _MALI_OSK_LOCKMODE_RW); + return MALI_TRUE; +} +static mali_bool set_mali_dvfs_status(u32 step,mali_bool boostup) +{ + u32 validatedStep=step; + +#ifdef CONFIG_REGULATOR + if (mali_regulator_get_usecount() == 0) { + MALI_DEBUG_PRINT(1, ("regulator use_count is 0 \n")); + return MALI_FALSE; + } +#endif + + if (boostup) { +#ifdef CONFIG_REGULATOR + /*change the voltage*/ + mali_regulator_set_voltage(mali_dvfs[step].vol, mali_dvfs[step].vol); +#endif + /*change the clock*/ + mali_clk_set_rate(mali_dvfs[step].clock, mali_dvfs[step].freq); + } else { + /*change the clock*/ + mali_clk_set_rate(mali_dvfs[step].clock, mali_dvfs[step].freq); +#ifdef CONFIG_REGULATOR + /*change the voltage*/ + mali_regulator_set_voltage(mali_dvfs[step].vol, mali_dvfs[step].vol); +#endif + } + + set_mali_dvfs_current_step(validatedStep); + /*for future use*/ + maliDvfsStatus.pCurrentDvfs = &mali_dvfs[validatedStep]; + + mali_set_runtime_resume_params(mali_dvfs[validatedStep].clock, + mali_dvfs[validatedStep].vol); + + return MALI_TRUE; +} + +static void mali_platform_wating(u32 msec) +{ + /*sample wating + change this in the future with proper check routine. + */ + unsigned int read_val; + while(1) { + read_val = _mali_osk_mem_ioread32(clk_register_map, 0x00); + if ((read_val & 0x8000)==0x0000) break; + _mali_osk_time_ubusydelay(100);// 1000 -> 100 : 20101218 + } + /* _mali_osk_time_ubusydelay(msec*1000);*/ +} + +static mali_bool change_mali_dvfs_status(u32 step, mali_bool boostup ) +{ + + MALI_DEBUG_PRINT(1, ("> change_mali_dvfs_status: %d, %d \n",step, + boostup)); + + if (!set_mali_dvfs_status(step, boostup)) { + MALI_DEBUG_PRINT(1, ("error on set_mali_dvfs_status: %d, %d \n" + ,step, boostup)); + return MALI_FALSE; + } + + /*wait until clock and voltage is stablized*/ + mali_platform_wating(MALI_DVFS_WATING); /*msec*/ + + return MALI_TRUE; +} + +#ifdef CONFIG_EXYNOS_ASV +extern unsigned int exynos4_result_of_asv; + +static mali_bool mali_dvfs_table_update(void) +{ + unsigned int i; + if(samsung_rev() < EXYNOS4412_REV_2_0) { +#if (MALI_DVFS_STEPS > 4) + mali_dvfs[4].vol = mali_dvfs[3].vol; + mali_dvfs[4].clock = mali_dvfs[3].clock; + for (i = 0; i < MALI_DVFS_STEPS - 1; i++) { +#else + for (i = 0; i < MALI_DVFS_STEPS ; i++) { +#endif + MALI_PRINT((":::exynos4_result_of_asv : %d\n", + exynos4_result_of_asv)); + mali_dvfs[i].vol = + asv_3d_volt_9_table[i][exynos4_result_of_asv]; + MALI_PRINT(("mali_dvfs[%d].vol = %d\n", i, + mali_dvfs[i].vol)); + } + } else { + for (i = 0; i < MALI_DVFS_STEPS; i++) { + MALI_PRINT((":::exynos_result_of_asv : %d \n", + exynos4_result_of_asv)); + mali_dvfs[i].vol = + asv_3d_volt_9_table_for_prime[i][exynos4_result_of_asv]; + MALI_PRINT(("mali_dvfs[%d].vol = %d\n", i, + mali_dvfs[i].vol)); + } + } + + return MALI_TRUE; +} +#endif + +static unsigned int decideNextStatus(unsigned int utilization) +{ + static unsigned int level = 0; // 0:stay, 1:up + + if (mali_dvfs_threshold[maliDvfsStatus.currentStep].upthreshold + <= mali_dvfs_threshold[maliDvfsStatus.currentStep].downthreshold) { + MALI_PRINT(("upthreadshold is smaller than downthreshold: %d < %d\n", + mali_dvfs_threshold[maliDvfsStatus.currentStep].upthreshold, + mali_dvfs_threshold[maliDvfsStatus.currentStep].downthreshold)); + return level; + } + + if (!mali_dvfs_control && level == maliDvfsStatus.currentStep) { + if (utilization > (int)(256 * mali_dvfs_threshold[maliDvfsStatus.currentStep].upthreshold / 100) && level < MALI_DVFS_STEPS - 1) { + level++; + } + if (utilization < (int)(256 * mali_dvfs_threshold[maliDvfsStatus.currentStep].downthreshold / 100) && + level > 0) { + level--; + } + } + + return level; +} + +static mali_bool mali_dvfs_status(u32 utilization) +{ + unsigned int nextStatus = 0; + unsigned int curStatus = 0; + mali_bool boostup = MALI_FALSE; + static int stay_count = 0; +#ifdef CONFIG_EXYNOS_ASV + static mali_bool asv_applied = MALI_FALSE; +#endif + + MALI_DEBUG_PRINT(1, ("> mali_dvfs_status: %d \n",utilization)); +#ifdef CONFIG_EXYNOS_ASV + if (asv_applied == MALI_FALSE) { + mali_dvfs_table_update(); + change_mali_dvfs_status(1, 0); + asv_applied = MALI_TRUE; + + return MALI_TRUE; + } +#endif + + /*decide next step*/ + curStatus = get_mali_dvfs_status(); + nextStatus = decideNextStatus(utilization); + + /*if next status is same with current status, don't change anything*/ + if ((curStatus != nextStatus && stay_count == 0)) { + /*check if boost up or not*/ + if (nextStatus > maliDvfsStatus.currentStep) boostup = 1; + + /*change mali dvfs status*/ + if (!change_mali_dvfs_status(nextStatus,boostup)) { + MALI_DEBUG_PRINT(1, ("error on change_mali_dvfs_status \n")); + return MALI_FALSE; + } + stay_count = mali_dvfs_staycount[maliDvfsStatus.currentStep].staycount; + } else { + if (stay_count > 0) + stay_count--; + } + + return MALI_TRUE; +} + +int mali_dvfs_is_running(void) +{ + return bMaliDvfsRun; + +} + +static void mali_dvfs_work_handler(struct work_struct *w) +{ + bMaliDvfsRun=1; + + MALI_DEBUG_PRINT(3, ("=== mali_dvfs_work_handler\n")); + + if (!mali_dvfs_status(mali_dvfs_utilization)) + MALI_DEBUG_PRINT(1,( "error on mali dvfs status in mali_dvfs_work_handler")); + + bMaliDvfsRun=0; +} + +mali_bool init_mali_dvfs_status(int step) +{ + /*default status + add here with the right function to get initilization value. + */ + if (!mali_dvfs_wq) + mali_dvfs_wq = create_singlethread_workqueue("mali_dvfs"); + + /*add a error handling here*/ + set_mali_dvfs_current_step(step); + + return MALI_TRUE; +} + +void deinit_mali_dvfs_status(void) +{ + if (mali_dvfs_wq) + destroy_workqueue(mali_dvfs_wq); + mali_dvfs_wq = NULL; +} + +mali_bool mali_dvfs_handler(u32 utilization) +{ + mali_dvfs_utilization = utilization; + queue_work_on(0, mali_dvfs_wq,&mali_dvfs_work); + + /*add error handle here*/ + return MALI_TRUE; +} + +#endif From ae267648ae0b85ed2f54b63332f970b0b9df9501 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sat, 14 Feb 2015 23:32:18 +0100 Subject: [PATCH 775/788] ARM: dts: odroid: specify GPU supply --- arch/arm/boot/dts/exynos4412-odroid-common.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index d73a19e15cbd02..2154078924173b 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -690,6 +690,7 @@ interrupt-names = "gp", "gp_mmu", "pp_0", "pp_mmu_0", "pp_1", "pp_mmu_1", "pp_2", "pp_mmu_2", "pp_3", "pp_mmu_3"; + gpu-supply = <&buck4_reg>; }; }; From f4fa7ec7bfabeaa3168647098d11799a42003db8 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sat, 14 Feb 2015 23:37:44 +0100 Subject: [PATCH 776/788] mali: platform: get regulator via DT specs --- drivers/gpu/arm/mali/platform/pegasus-m400/pegasus-m400.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/gpu/arm/mali/platform/pegasus-m400/pegasus-m400.c b/drivers/gpu/arm/mali/platform/pegasus-m400/pegasus-m400.c index 745aede5383f1c..67a35add5840ce 100644 --- a/drivers/gpu/arm/mali/platform/pegasus-m400/pegasus-m400.c +++ b/drivers/gpu/arm/mali/platform/pegasus-m400/pegasus-m400.c @@ -119,11 +119,7 @@ static int mali_platform_init_clk(void) if (mali_platform_clk_enable()) return 1; #ifdef CONFIG_REGULATOR -#ifdef USING_MALI_PMM - g3d_regulator = regulator_get(&mali_platform_device.dev, "vdd_g3d"); -#else - g3d_regulator = regulator_get(NULL, "vdd_g3d"); -#endif + g3d_regulator = regulator_get(&(mali_platform_device->dev), "gpu"); if (IS_ERR(g3d_regulator)) { MALI_PRINT_ERROR(("Mali platform: failed to get g3d regulator\n")); From 694b40f1566120af888c682c953fd635eb6b4ca2 Mon Sep 17 00:00:00 2001 From: Mauro Ribeiro Date: Tue, 4 Mar 2014 06:46:39 -0300 Subject: [PATCH 777/788] drm/exynos: implement GET_FB_DMA_BUF ioctl for Mali interoperaton Enables triple buffering and dmabuf support according to Mali documentation. Code is currently disabled via preprocessor macro. --- drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 57 +++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index 84f8dfe1c5ec02..f7b66617d149be 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -27,6 +27,12 @@ #define MAX_CONNECTOR 4 #define PREFERRED_BPP 32 +#if 0 +#include +#define IOCTL_GET_FB_DMA_BUF _IOWR('m', 0xF9, __u32) +#define NUM_BUFFERS 3 +#endif + #define to_exynos_fbdev(x) container_of(x, struct exynos_drm_fbdev,\ drm_fb_helper) @@ -62,6 +68,50 @@ static int exynos_drm_fb_mmap(struct fb_info *info, return 0; } +#if 0 +static u32 exynos_fb_get_dma_buf(struct fb_info *info) +{ + int fd = -1; + struct drm_fb_helper *helper = info->par; + struct drm_device *dev = helper->dev; + struct exynos_drm_fbdev *exynos_fbd = to_exynos_fbdev(helper); + struct exynos_drm_gem_obj *exynos_gem_obj = exynos_fbd->exynos_gem_obj; + + if (dev->driver->gem_prime_export) { + struct dma_buf *buf = NULL; + buf = dev->driver->gem_prime_export(dev, &exynos_gem_obj->base, O_RDWR); + if (buf) + fd = dma_buf_fd(buf, O_RDWR); + } + + return fd; +} + +static int fb_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg) +{ + int ret; + + switch (cmd) { + case IOCTL_GET_FB_DMA_BUF: + { + u32 __user *out_ptr = (u32 __user *)arg; + u32 buf_fd = exynos_fb_get_dma_buf(info); + if (buf_fd == -1) { + ret = -ENOMEM; + break; + } + ret = put_user(buf_fd, out_ptr); + break; + } + default: + ret = -ENOTTY; + } + + return ret; +} +#endif + static struct fb_ops exynos_drm_fb_ops = { .owner = THIS_MODULE, .fb_mmap = exynos_drm_fb_mmap, @@ -73,6 +123,9 @@ static struct fb_ops exynos_drm_fb_ops = { .fb_blank = drm_fb_helper_blank, .fb_pan_display = drm_fb_helper_pan_display, .fb_setcmap = drm_fb_helper_setcmap, +#if 0 + .fb_ioctl = fb_ioctl, +#endif }; static int exynos_drm_fbdev_update(struct drm_fb_helper *helper, @@ -134,7 +187,11 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper, sizes->surface_bpp); mode_cmd.width = sizes->surface_width; +#if 0 + mode_cmd.height = sizes->surface_height * NUM_BUFFERS; +#else mode_cmd.height = sizes->surface_height; +#endif mode_cmd.pitches[0] = sizes->surface_width * (sizes->surface_bpp >> 3); mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth); From d2cd77001a747d5253dd82f2d1b9fca2cacfaaf2 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Tue, 17 Feb 2015 14:58:10 +0100 Subject: [PATCH 778/788] ARM: dts: odroid: Use 400MHz clock for the G2D engine --- arch/arm/boot/dts/exynos4412-odroid-common.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index 2154078924173b..537002315ea101 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -148,6 +148,10 @@ g2d@10800000 { status = "okay"; + + /* Clock the engine with 400MHz. */ + assigned-clocks = <&clock CLK_SCLK_FIMG2D>; + assigned-clock-rates = <400000000>; }; camera { From c3b3ad85f83f0d8e61c14987644f857b1746b827 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Tue, 17 Feb 2015 16:14:27 +0100 Subject: [PATCH 779/788] PM / devfreq: fix governor_store --- drivers/devfreq/devfreq.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 30b538d8cc90a5..466a685b830f49 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -796,8 +796,10 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr, ret = PTR_ERR(governor); goto out; } - if (df->governor == governor) + if (df->governor == governor) { + ret = 0; goto out; + } if (df->governor) { ret = df->governor->event_handler(df, DEVFREQ_GOV_STOP, NULL); From 697daec97ec1f80e70cf55bab5b97c81c78dd658 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Tue, 15 Jul 2014 16:33:45 +0100 Subject: [PATCH 780/788] exynos4-is: don't fail if no ports are found Some boards do not have any accessible camera ports, but would still like to use the internal color conversion functionality of the FIMC. Don't fail probe in that situation. --- drivers/media/platform/exynos4-is/media-dev.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index f315ef946cd423..502556514d07f2 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -437,15 +437,15 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd) /* Attach sensors listed in the parallel-ports node */ ports = of_get_child_by_name(parent, "parallel-ports"); - if (!ports) - goto rpm_put; - - for_each_child_of_node(ports, node) { - ret = fimc_md_parse_port_node(fmd, node, index); - if (ret < 0) - break; - index++; + if (ports) { + for_each_child_of_node(ports, node) { + ret = fimc_md_parse_port_node(fmd, node, index); + if (ret < 0) + break; + index++; + } } + rpm_put: pm_runtime_put(fmd->pmf); return ret; From 7ddabc276d51abbf4add3bf4e2a14aaf08ed5951 Mon Sep 17 00:00:00 2001 From: Beata Michalska Date: Wed, 4 Mar 2015 15:02:49 +0100 Subject: [PATCH 781/788] drm/exynos/ipp: Validate buffer enqueue requests As for now there is no validation of incoming buffer enqueue request as far as the gem buffers are being concerned. This might lead to some undesired cases when the driver tries to operate on invalid buffers (wiht no valid gem object handle i.e.). Add some basic checks to rule out those potential issues. Signed-off-by: Beata Michalska [mszyprow: rebased onto v4.0-rc1 and adapted to recent ipp changes] Signed-off-by: Marek Szyprowski --- drivers/gpu/drm/exynos/exynos_drm_ipp.c | 44 +++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.c b/drivers/gpu/drm/exynos/exynos_drm_ipp.c index d5ad17dfc24ddc..b7f1cbc46cc2c3 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_ipp.c +++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.c @@ -476,6 +476,45 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data, return ret; } +static int ipp_validate_mem_node(struct drm_device *drm_dev, + struct drm_exynos_ipp_mem_node *m_node, + struct drm_exynos_ipp_cmd_node *c_node) +{ + struct drm_exynos_ipp_config *ipp_cfg; + unsigned int num_plane; + unsigned long min_size, size; + unsigned int bpp; + int i; + + /* The property id should already be varified */ + ipp_cfg = &c_node->property.config[m_node->prop_id]; + num_plane = drm_format_num_planes(ipp_cfg->fmt); + + /** + * This is a rather simplified validation of a memory node. + * It basically verifies provided gem object handles + * and the buffer sizes with respect to current configuration. + * This is not the best that can be done + * but it seems more than enough + */ + for (i = 0; i < num_plane; ++i) { + if (!m_node->buf_info.handles[i]) { + DRM_ERROR("invalid handle for plane %d\n", i); + return -EINVAL; + } + bpp = drm_format_plane_cpp(ipp_cfg->fmt, i); + min_size = (ipp_cfg->sz.hsize * ipp_cfg->sz.vsize * bpp) >> 3; + size = exynos_drm_gem_get_size(drm_dev, + m_node->buf_info.handles[i], + c_node->filp); + if (min_size > size) { + DRM_ERROR("invalid size for plane %d\n", i); + return -EINVAL; + } + } + return 0; +} + static int ipp_put_mem_node(struct drm_device *drm_dev, struct drm_exynos_ipp_cmd_node *c_node, struct drm_exynos_ipp_mem_node *m_node) @@ -552,6 +591,11 @@ static struct drm_exynos_ipp_mem_node } mutex_lock(&c_node->mem_lock); + if (ipp_validate_mem_node(drm_dev, m_node, c_node)) { + ipp_put_mem_node(drm_dev, c_node, m_node); + mutex_unlock(&c_node->mem_lock); + return ERR_PTR(-EFAULT); + } list_add_tail(&m_node->list, &c_node->mem_list[qbuf->ops_id]); mutex_unlock(&c_node->mem_lock); From 1ad57c4b955c76e0c568e18d19473a00f6d084c6 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 3 Mar 2015 09:08:43 +0800 Subject: [PATCH 782/788] phy: samsung-usb2: Remove NULL terminating entry from phys array Current code uses num_phys settings to tell the number of entries in phys. Thus remove the NULL terminating entry from phys array which is not necessary. Signed-off-by: Axel Lin Acked-by: Kamil Debski --- drivers/phy/phy-exynos4210-usb2.c | 1 - drivers/phy/phy-exynos4x12-usb2.c | 1 - drivers/phy/phy-exynos5250-usb2.c | 1 - 3 files changed, 3 deletions(-) diff --git a/drivers/phy/phy-exynos4210-usb2.c b/drivers/phy/phy-exynos4210-usb2.c index 236a52ad94eb70..f30bbb0fb3b265 100644 --- a/drivers/phy/phy-exynos4210-usb2.c +++ b/drivers/phy/phy-exynos4210-usb2.c @@ -250,7 +250,6 @@ static const struct samsung_usb2_common_phy exynos4210_phys[] = { .power_on = exynos4210_power_on, .power_off = exynos4210_power_off, }, - {}, }; const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = { diff --git a/drivers/phy/phy-exynos4x12-usb2.c b/drivers/phy/phy-exynos4x12-usb2.c index 0b9de88579b132..765da90a536f05 100644 --- a/drivers/phy/phy-exynos4x12-usb2.c +++ b/drivers/phy/phy-exynos4x12-usb2.c @@ -361,7 +361,6 @@ static const struct samsung_usb2_common_phy exynos4x12_phys[] = { .power_on = exynos4x12_power_on, .power_off = exynos4x12_power_off, }, - {}, }; const struct samsung_usb2_phy_config exynos3250_usb2_phy_config = { diff --git a/drivers/phy/phy-exynos5250-usb2.c b/drivers/phy/phy-exynos5250-usb2.c index 1c139aa0d07432..2ed1735a076a1f 100644 --- a/drivers/phy/phy-exynos5250-usb2.c +++ b/drivers/phy/phy-exynos5250-usb2.c @@ -391,7 +391,6 @@ static const struct samsung_usb2_common_phy exynos5250_phys[] = { .power_on = exynos5250_power_on, .power_off = exynos5250_power_off, }, - {}, }; const struct samsung_usb2_phy_config exynos5250_usb2_phy_config = { From 66a6a5ac649ea39bae32e4f4d323b87c5788a3b2 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Thu, 5 Mar 2015 13:48:40 +0100 Subject: [PATCH 783/788] ARM: dts: exynos4: add nodes for jpeg codec This patch adds nodes for hardware JPEG codec found in Exynos 4210 and 4x12 SoCs. Signed-off-by: Marek Szyprowski Reviewed by: Javier Martinez Canillas --- arch/arm/boot/dts/exynos4.dtsi | 9 +++++++++ arch/arm/boot/dts/exynos4x12.dtsi | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi index 526fb2fb2298e5..316bbf7403c4a7 100644 --- a/arch/arm/boot/dts/exynos4.dtsi +++ b/arch/arm/boot/dts/exynos4.dtsi @@ -695,6 +695,15 @@ clock-names = "secss"; }; + jpeg-codec@11840000 { + compatible = "samsung,exynos4210-jpeg"; + reg = <0x11840000 0x1000>; + interrupts = <0 88 0>; + clocks = <&clock CLK_JPEG>; + clock-names = "jpeg"; + power-domains = <&pd_cam>; + }; + hdmi: hdmi@12D00000 { compatible = "samsung,exynos4210-hdmi"; reg = <0x12D00000 0x70000>; diff --git a/arch/arm/boot/dts/exynos4x12.dtsi b/arch/arm/boot/dts/exynos4x12.dtsi index 3f0071c03c4dd2..e0d6568ac3035f 100644 --- a/arch/arm/boot/dts/exynos4x12.dtsi +++ b/arch/arm/boot/dts/exynos4x12.dtsi @@ -426,6 +426,10 @@ status = "disabled"; }; + jpeg-codec@11840000 { + compatible = "samsung,exynos4212-jpeg"; + }; + hdmi: hdmi@12D00000 { compatible = "samsung,exynos4212-hdmi"; }; From fdf27e53676cd89826f8c962be4a82f188250181 Mon Sep 17 00:00:00 2001 From: Nicolas Dufresne Date: Fri, 21 Mar 2014 17:15:17 -0400 Subject: [PATCH 784/788] s5p-fimc: Align imagesize to row size for tiled formats For tiled format, we need to allocate a multiple of the row size. A good example is 1280x720, which gets adjusted to 1280x736. In tiles this means Y plane is 20x23 and UV plane is 20x12. Because of the rounding, the previous code would only have enough space to fit half of the last row. --- drivers/media/platform/exynos4-is/fimc-core.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/exynos4-is/fimc-core.c b/drivers/media/platform/exynos4-is/fimc-core.c index 1101c41ac11788..e2d81bb0f62915 100644 --- a/drivers/media/platform/exynos4-is/fimc-core.c +++ b/drivers/media/platform/exynos4-is/fimc-core.c @@ -736,6 +736,7 @@ void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height, for (i = 0; i < pix->num_planes; ++i) { struct v4l2_plane_pix_format *plane_fmt = &pix->plane_fmt[i]; u32 bpl = plane_fmt->bytesperline; + u32 sizeimage; if (fmt->colplanes > 1 && (bpl == 0 || bpl < pix->width)) bpl = pix->width; /* Planar */ @@ -755,8 +756,16 @@ void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height, bytesperline /= 2; plane_fmt->bytesperline = bytesperline; - plane_fmt->sizeimage = max((pix->width * pix->height * - fmt->depth[i]) / 8, plane_fmt->sizeimage); + sizeimage = pix->width * pix->height * fmt->depth[i] / 8; + + /* Ensure full row for tiled formats */ + if (tiled_fmt(fmt)) { + /* 64 * 32 * plane_fmt->bytesperline / 64 */ + u32 row_size = plane_fmt->bytesperline * 32; + sizeimage = ALIGN(sizeimage, row_size); + } + + plane_fmt->sizeimage = max(sizeimage, plane_fmt->sizeimage); } } From 2aaebd6cc097f96fc5a4086ed6f14803eab90cf9 Mon Sep 17 00:00:00 2001 From: Nicolas Dufresne Date: Wed, 26 Mar 2014 18:39:52 -0400 Subject: [PATCH 785/788] s5p-fimc: Use roundup() instead of ALIGN() The size of a row is not always a power of two, so ALIGN cannot be used. --- drivers/media/platform/exynos4-is/fimc-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/exynos4-is/fimc-core.c b/drivers/media/platform/exynos4-is/fimc-core.c index e2d81bb0f62915..949b82538fad3f 100644 --- a/drivers/media/platform/exynos4-is/fimc-core.c +++ b/drivers/media/platform/exynos4-is/fimc-core.c @@ -762,7 +762,7 @@ void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height, if (tiled_fmt(fmt)) { /* 64 * 32 * plane_fmt->bytesperline / 64 */ u32 row_size = plane_fmt->bytesperline * 32; - sizeimage = ALIGN(sizeimage, row_size); + sizeimage = roundup(sizeimage, row_size); } plane_fmt->sizeimage = max(sizeimage, plane_fmt->sizeimage); From 3fac0433cc3c3ec124792a82b36073833a4d89f3 Mon Sep 17 00:00:00 2001 From: Hyungwon Hwang Date: Thu, 12 Mar 2015 23:10:41 +0900 Subject: [PATCH 786/788] drm/exynos: fix the number of overlay planes The number of overlay planes is one less than the maximum number of planes, because the one is used for primary plane. Signed-off-by: Hyungwon Hwang --- drivers/gpu/drm/exynos/exynos_drm_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 1bcbe07cecfc95..353bb29978843e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -81,7 +81,7 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags) exynos_drm_mode_config_init(dev); - for (nr = 0; nr < MAX_PLANE; nr++) { + for (nr = 0; nr < MAX_PLANE - 1; nr++) { struct drm_plane *plane; unsigned long possible_crtcs = (1 << MAX_CRTC) - 1; From b1daa3772703425df495d744db1137167c42ac6f Mon Sep 17 00:00:00 2001 From: Damian Eppel Date: Thu, 12 Mar 2015 10:11:36 +0100 Subject: [PATCH 787/788] clocksource: exynos_mct: fix for sleeping in atomic ctx handling cpu hotplug notif. This is to fix an issue of sleeping in atomic context when processing hotplug notifications in Exynos MCT(Multi-Core Timer). The issue was reproducible on Exynos 3250 (Rinato board) and Exynos 5420 (Arndale Octa board). Whilst testing cpu hotplug events on kernel configured with DEBUG_PREEMPT and DEBUG_ATOMIC_SLEEP we get following BUG message, caused by calling request_irq() and free_irq() in the context of hotplug notification (which is in this case atomic context). root@target:~# echo 0 > /sys/devices/system/cpu/cpu1/online [ 25.157867] IRQ18 no longer affine to CPU1 ... [ 25.158445] CPU1: shutdown root@target:~# echo 1 > /sys/devices/system/cpu/cpu1/online [ 40.785859] CPU1: Software reset [ 40.786660] BUG: sleeping function called from invalid context at mm/slub.c:1241 [ 40.786668] in_atomic(): 1, irqs_disabled(): 128, pid: 0, name: swapper/1 [ 40.786678] Preemption disabled at:[< (null)>] (null) [ 40.786681] [ 40.786692] CPU: 1 PID: 0 Comm: swapper/1 Not tainted 3.19.0-rc4-00024-g7dca860 #36 [ 40.786698] Hardware name: SAMSUNG EXYNOS (Flattened Device Tree) [ 40.786728] [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [ 40.786747] [] (show_stack) from [] (dump_stack+0x70/0xbc) [ 40.786767] [] (dump_stack) from [] (kmem_cache_alloc+0xd8/0x170) [ 40.786785] [] (kmem_cache_alloc) from [] (request_threaded_irq+0x64/0x128) [ 40.786804] [] (request_threaded_irq) from [] (exynos4_local_timer_setup+0xc0/0x13c) [ 40.786820] [] (exynos4_local_timer_setup) from [] (exynos4_mct_cpu_notify+0x30/0xa8) [ 40.786838] [] (exynos4_mct_cpu_notify) from [] (notifier_call_chain+0x44/0x84) [ 40.786857] [] (notifier_call_chain) from [] (__cpu_notify+0x28/0x44) [ 40.786873] [] (__cpu_notify) from [] (secondary_start_kernel+0xec/0x150) [ 40.786886] [] (secondary_start_kernel) from [<40008764>] (0x40008764) Solution: Clockevent irqs cannot be requested/freed every time cpu is hot-plugged/unplugged as CPU_STARTING/CPU_DYING notifications that signals hotplug or unplug events are sent with both preemption and local interrupts disabled. Since request_irq() may sleep it is moved to the initialization stage and performed for all possible cpus prior putting them online. Then, in the case of hotplug event the irq asociated with the given cpu will simply be enabled. Similarly on cpu unplug event the interrupt is not freed but just disabled. Note that after successful request_irq() call for a clockevent device associated to given cpu the requested irq is disabled (via disable_irq()). That is to make things symmetric as we expect hotplug event as a next thing (which will enable irq again). This should not pose any problems because at the time of requesting irq the clockevent device is not fully initialized yet, therefore should not produce interrupts at that point. For disabling an irq at cpu unplug notification disable_irq_nosync() is chosen which is a non-blocking function. This again shouldn't be a problem as prior sending CPU_DYING notification interrupts are migrated away from the cpu. Fixes: 7114cd749a12 ("clocksource: exynos_mct: use (request/free)_irq calls for local timer registration") Signed-off-by: Damian Eppel Cc: Reported-by: Krzysztof Kozlowski Reviewed-by: Krzysztof Kozlowski Tested-by: Krzysztof Kozlowski (Tested on Arndale Octa Board) Tested-by: Marcin Jabrzyk (Tested on Rinato B2 (Exynos 3250) board) --- drivers/clocksource/exynos_mct.c | 45 +++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c index 83564c9cfdbe3b..9beca58347a66b 100644 --- a/drivers/clocksource/exynos_mct.c +++ b/drivers/clocksource/exynos_mct.c @@ -466,15 +466,12 @@ static int exynos4_local_timer_setup(struct clock_event_device *evt) exynos4_mct_write(TICK_BASE_CNT, mevt->base + MCT_L_TCNTB_OFFSET); if (mct_int_type == MCT_INT_SPI) { - evt->irq = mct_irqs[MCT_L0_IRQ + cpu]; - if (request_irq(evt->irq, exynos4_mct_tick_isr, - IRQF_TIMER | IRQF_NOBALANCING, - evt->name, mevt)) { - pr_err("exynos-mct: cannot register IRQ %d\n", - evt->irq); + + if (evt->irq == -1) return -EIO; - } - irq_force_affinity(mct_irqs[MCT_L0_IRQ + cpu], cpumask_of(cpu)); + + irq_force_affinity(evt->irq, cpumask_of(cpu)); + enable_irq(evt->irq); } else { enable_percpu_irq(mct_irqs[MCT_L0_IRQ], 0); } @@ -487,10 +484,12 @@ static int exynos4_local_timer_setup(struct clock_event_device *evt) static void exynos4_local_timer_stop(struct clock_event_device *evt) { evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt); - if (mct_int_type == MCT_INT_SPI) - free_irq(evt->irq, this_cpu_ptr(&percpu_mct_tick)); - else + if (mct_int_type == MCT_INT_SPI) { + if (evt->irq != -1) + disable_irq_nosync(evt->irq); + } else { disable_percpu_irq(mct_irqs[MCT_L0_IRQ]); + } } static int exynos4_mct_cpu_notify(struct notifier_block *self, @@ -522,7 +521,7 @@ static struct notifier_block exynos4_mct_cpu_nb = { static void __init exynos4_timer_resources(struct device_node *np, void __iomem *base) { - int err; + int err, cpu; struct mct_clock_event_device *mevt = this_cpu_ptr(&percpu_mct_tick); struct clk *mct_clk, *tick_clk; @@ -549,7 +548,27 @@ static void __init exynos4_timer_resources(struct device_node *np, void __iomem WARN(err, "MCT: can't request IRQ %d (%d)\n", mct_irqs[MCT_L0_IRQ], err); } else { - irq_set_affinity(mct_irqs[MCT_L0_IRQ], cpumask_of(0)); + for_each_possible_cpu(cpu) { + int mct_irq = mct_irqs[MCT_L0_IRQ + cpu]; + struct mct_clock_event_device *pcpu_mevt = + per_cpu_ptr(&percpu_mct_tick, cpu); + + pcpu_mevt->evt.irq = -1; + + if (request_irq(mct_irq, + exynos4_mct_tick_isr, + IRQF_TIMER | IRQF_NOBALANCING, + pcpu_mevt->name, pcpu_mevt)) { + pr_err("exynos-mct: cannot register IRQ (cpu%d)\n", + cpu); + + continue; + } + pcpu_mevt->evt.irq = mct_irq; + irq_force_affinity(mct_irq, cpumask_of(cpu)); + + disable_irq(mct_irq); + } } err = register_cpu_notifier(&exynos4_mct_cpu_nb); From 684c5f18e43b200d07f8aa347ae0836d8d1e4076 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 17 Mar 2015 08:42:38 +0800 Subject: [PATCH 788/788] phy: samsung_usb2: Fixup samsung_usb2_phy_power_on/off paths Ensure we have balanced clk_prepare_enable/clk_disable_unprepare calls if .power_on or .power_off callbacks return error. Signed-off-by: Axel Lin --- drivers/phy/phy-samsung-usb2.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/phy/phy-samsung-usb2.c b/drivers/phy/phy-samsung-usb2.c index 4a12f66b7fb5f7..55b6994932e37f 100644 --- a/drivers/phy/phy-samsung-usb2.c +++ b/drivers/phy/phy-samsung-usb2.c @@ -37,10 +37,14 @@ static int samsung_usb2_phy_power_on(struct phy *phy) spin_lock(&drv->lock); ret = inst->cfg->power_on(inst); spin_unlock(&drv->lock); + if (ret) + goto err_power_on; } return 0; +err_power_on: + clk_disable_unprepare(drv->ref_clk); err_instance_clk: clk_disable_unprepare(drv->clk); err_main_clk: @@ -51,7 +55,7 @@ static int samsung_usb2_phy_power_off(struct phy *phy) { struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy); struct samsung_usb2_phy_driver *drv = inst->drv; - int ret = 0; + int ret; dev_dbg(drv->dev, "Request to power_off \"%s\" usb phy\n", inst->cfg->label); @@ -59,10 +63,12 @@ static int samsung_usb2_phy_power_off(struct phy *phy) spin_lock(&drv->lock); ret = inst->cfg->power_off(inst); spin_unlock(&drv->lock); + if (ret) + return ret; } clk_disable_unprepare(drv->ref_clk); clk_disable_unprepare(drv->clk); - return ret; + return 0; } static struct phy_ops samsung_usb2_phy_ops = {